简体   繁体   中英

howto get exit code of script running within expect

for some time I am struggling to get the exit code of a script which I am running from within expect. It is a BASH script and the questionable part looks like this:

  expect -c "
    log_file $LOG
    spawn su - $ora_user
    expect ""
    send \"source $oraenv_binary\r\"
    expect \"ORACLE_SID = \[$ora_user\] \?\"
    send \"$SID\r\"
    expect \"The Oracle base has been set to /oracle/$SID\"
    send \"$execPATHroot/$subscript $args_subscript\r\"
    expect ""
    send \"echo \$?\r\"
    expect -re \"(\\d+)\" {
    set result \$expect_out(1,string)
    }
    send_user \"subscript exit code: \$result\"
    log_file
    send \"exit\r\"
    expect ""
    exit [lindex \$result 3]"
    sub_rc=$?

Needed to say that this is one of many tries to get the code, however, unsuccessfully. I guess that my problem lies in incorrectly escaped characters or wrong use of brackets.....

When debugging, I am getting the following:

[336] oraenv_binary=/usr/local/bin/oraenv
[338] expect -c '
    log_file /var/opt/osit/oracle/log/ora_sbp_patching_root.bash.log
    spawn su - oracle
    expect
    send "source /usr/local/bin/oraenv\r"
    expect "ORACLE_SID = \[oracle\] \?"
    send "H95\r"
    expect "The Oracle base has been set to /oracle/H95"
    send "/opt/osit/oracle/bin/ora_sbp_patching_orausr.bash -s H95 -a CHECK -p /imports/e2r2s48ifde0002/CDSAP/DB/oracle/ORA19/SBP/SBP_1915_220419_202205 -h /imports/e2r2s48ifde0002/CDSAP/DB/oracle/ORA19/SBP/SBP_1915_220419_202205/README19P_2205-70004508.HTM -u oracle\r"
    expect
    send "echo $?\r"
    expect -re "(\d+)" {
    set result $expect_out(1,string)
    }
    send_user "subscript exit code: $result"
    log_file
    send "exit\r"
    expect
    exit [lindex $result 3]'

.....subscript runs here OK with exit code 0 in this case

-sh-4.2$ subscript exit code: decho $?
0
-sh-4.2$ exit
logout
expected integer but got ""
    while executing
"exit [lindex $result 3]"
[357] sub_rc=0

It seems to me that the regex part "(\d+)" is not OK, but perhaps, it is completely a mess... :-) Please help.

I have read and tried these recommendations: Is there a way to expect output without removing it from the buffer in Tcl expect?

https://wiki.tcl-lang.org/page/How+Expect+can+capture+the+exit+code+from+a+remote+command

https://www.unix.com/shell-programming-and-scripting/144812-expect-script-obtain-exit-code-remote-command.html

You have this

    expect -re "(\d+)" {
    set result $expect_out(1,string)
    }
    send_user "subscript exit code: $result"

and we can see the output is

-sh-4.2$ subscript exit code: decho $?
0

Because the regular expression "(\d+)" is in double quotes , backslash substitutions will occur, and the pattern becomes (d+) which may not match (do you get a 10 second delay at that point?) -- I suspect this is why $result is empty.

Backslashes are prevalent in regular expressions. Using braces to quote them is the way to go:

expect -re {\d+} {set result $expect_out(0,string)}

Running your expect code with expect -d (or set exp_internal 1 in the code) emits very verbose expect debug output that is extremely useful to see how your patterns are matching (or not).


Using quoted shell heredocs is (IMO) preferable to using quoted strings to encapsulate code.

Consider

expect -c "
send_user \"my home is \$env(HOME)\\n\"
"

versus

expect << 'END_CODE'
    send_user "my home is $env(HOME)\n"
END_CODE

With this technique, you pass shell variable to expect through the environment:

export ora_user=oracle

expect << 'END_EXPECT'
    #...
    spawn su - $env(ora_user)
END_EXPECT

thanks a lot for your answer Glenn, interesting points mentioned. Regarding the braces versus double quotes - I have changed it like that, but no effect. I have those double quotes escaped by backslash within my code - I think the effect is the same, however, definitely it looks nicer to me and evidently it is safer. I have played with the debug mode of expect - thanks for that, I can see much more info. I have noticed that expect holds much more stuff than I "expected":-)

==> this is just a snippet:

.\r\n-sh-4.2$ " (spawn_id exp7) match regular expression "\d+"? (No Gate, RE only) gate=yes re=yes
expect: set expect_out(0,string) "4"
expect: set expect_out(spawn_id) "exp7"
expect: set expect_out(buffer) "\r\n-sh-4"
can't read "expect_out(1,string)": no such element in array
    while executing
"set result $expect_out(1,string)"
    invoked from within
"expect -re {\d+} {set result $expect_out(1,string)}"

As you can see, when I am sending the subscript to be executed I am expecting "" ie nothing, just the new prompt line. However, at that point expect is full of stuff, not at all blank - I think, I need to define the prompt exactly:

-sh-4.2$

and then I need to expect it, together with echoed exit code $? and somehow separate exit code integer to get what I want...... I will keep trying.

The final code which works is as follows (much of the credit goes to Glenn for his numerous advices):

expect -c "
log_file $LOG
spawn su - $ora_user
expect -re {\$ $}
send \"PS1='>'\r\"
expect -re {>$}
send \"source $oraenv_binary\r\"
expect {ORACLE_SID = \[$ora_user\] ? }
send \"$SID\r\"
expect \"The Oracle base has been set to /oracle/$SID\"
send \"$execPATHroot/$subscript $args_subscript\r\"
expect { (subscript) ; exp_continue }
expect -re {>$}
send \"echo \$?\r\"
expect -re {(\d+)\r\n>$} {set result \$expect_out(1,string)}
send_user \"subscript exit code:\$result\"
log_file
send \"\rexit\r\"
expect \"logout\"
exit [lindex \$result 0]"
sub_rc=$?
echo sub_rc:$sub_rc

The first thing after spawn su - $ora_user is to set the prompt by send \"PS1='>'\r\" in order to make new lines with prompt less intrusive to expect.

Then after send \"$execPATHroot/$subscript $args_subscript\r\" I have used the fact, that I have written the subscript to have every line of output populated by (subscript) keyword. So while the subscript produces the output, the expect keeps going by exp_continue .

When the $subscript ends, the prompt > appears into which the expect sends echo $? to get exit code of the $subscript . This appears on the screen as:

>echo $?
0
>

so the code should expect the integer, return and the new line with prompt - ie {(\d+)\r\n>$} . At that time the expect matches the output and expect_out(1,string) is correctly populated:

send: sending "echo $?\r" to { exp7 }
Gate keeper glob pattern for '(\d+)\r\n>$' is '*
>'. Activating booster.
expect: does "" (spawn_id exp7) match regular expression "(\d+)\r\n>$"? Gate "*\r\n>"? gate=no
echo $?
0
>
expect: does "echo $?\r\n0\r\n>" (spawn_id exp7) match regular expression "(\d+)\r\n>$"? Gate "*\r\n>"? gate=yes re=yes
expect: set expect_out(0,string) "0\r\n>"
expect: set expect_out(1,string) "0"
expect: set expect_out(spawn_id) "exp7"
expect: set expect_out(buffer) "echo $?\r\n0\r\n>"

Aother thing to mention is \r within send \"\rexit\r\" as the preceding send_user \"subscript exit code:\$result\" does not result in new line and prompt.

The last change to the code in question is:

exit [lindex \$result 0]"

I have changed the index to 0 as variable result has just one item and index 0 stands for 1st item in the list.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM