Search code examples
bashtclexpect

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


Solution

  • 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\n\"
    log_file
    send \"exit\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 - i.e. {(\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>"
    

    Another thing to mention is \n within send_user \"subscript exit code:\$result\n\" so to have new line next..

    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.