Broken expect/TCL script

uncle-junky

I have a Bash script which includes an expect/TCL EOF script in a separate function. The expect script exits with 0-4 possible exit codes depending on what the expect/TCL script determines from the remote device, and within an if ... elif ... else statement I write a specific string to a variable depending on this exit code (within that expect/TCL function). Control is then passed back to the Bash script where a case block runs on the string contained within said variable contains.

The trouble I am experiencing is that my Bash function which contains the expect/TCL script does not catch exit code 3 (well actually it does as I can see the log file entry correctly writes to the log file, but if I echo the value of the exit code it actually catches a zero when the situation should be a 3) and thus my case statement is not switching accurately.

Can you find the bug?

(I've chopped the script right down to just these parts for purpose of keeping the post concise and specific so just assume that the surrounding code is running OK).

function myTclFunc()
{
    /usr/bin/expect<<EOF
    proc log_msg {msg {to_stdout no}} {
        set log_line "[timestamp -format {[%d/%m/%Y @ %T]}] \$msg"
        set fh [open ~/mylogfile.log a]
        puts \$fh \$log_line
        close \$fh
        if {\$to_stdout} {puts \$log_line}
    }

    ;#exp_internal 1
    set timeout 5
    set send_human {.1 .3 1 .05 2}

    spawn ssh -o "StrictHostKeyChecking no" "[USER]@$1"

    expect {
        "password: " { send -h "[MY_PASSWD]\r" }
        timeout { log_msg "A RELEVANT STRING TO LOG $1 / $2"; exit 1 }
    }

    set timeout 3
    sleep 1 ;
    send -h "[COMMAND A]\r" ;
    expect {
        timeout { exit 1 }
        -re {\m\d{1,}(\.\d{1,}){3}\M}
    }
    if { ! [regexp {192\.[0-9]{1,3}\.{2}[0-9]{1,3}} $expect_out(0,string)]} {
        send -h "[COMMAND B]\r" ;
    }
    expect {
        "[STRING 1]" { 
            send -h "[COMMAND C]\r" ;
            log_msg "Problem F on $1 / $2" ;
            exit 3
        }
        "[STRING 2]" { 
            send -h "[COMMAND D]\r" ;
            sleep 1 ;
            send -h "[COMMAND E]\r" ;
            send -h "\r" ;
            puts "\r"
            ;#exit 0
        }
    }
    expect eof
EOF
    if [[ $? -eq 0 ]]; then
        passBack="GOOD";
    elif [[ $? -eq 3 ]]; then
        passBack="BAD";
    else
        passBack="TIMEOUT";
    fi;
}

[...snipped code...]
myTclFunc $myVar $1

case "$passBack" in
GOOD)
    echo ""
    exit 0
    ;;
BAD)
    echo ""
    exit 4
    ;;
CHECK)
    echo ""
    exit 3
    ;;
esac;
[...snipped code...]
Donal Fellows

The mixture of Shell and Tcl can be a bit tricky at times. In particular, you are wanting to deliver \ into the regular expression engine in a few places, inside a here-document with an unquoted delimiter word. Fortunately, you're otherwise putting the REs inside braces, so it's just confusing and not super-confusing!

The relevant part of the bash documentation says:

Here Documents

This type of redirection instructs the shell to read input from the current source until a line containing only word (with no trailing blanks) is seen. All of the lines read up to that point are then used as the standard input for a command.

The format of here-documents is:

   <<[-]word
       here-document
   delimiter

No parameter expansion, command substitution, arithmetic expansion, or pathname expansion is performed on word. If any characters in word are quoted, the delimiter is the result of quote removal on word, and the lines in the here-document are not expanded. If word is unquoted, all lines of the here-document are subjected to parameter expansion, command substitution, and arithmetic expansion. In the latter case, the character sequence \<newline> is ignored, and \ must be used to quote the characters \, $, and `.

If the redirection operator is <<-, then all leading tab characters are stripped from input lines and the line containing delimiter. This allows here-documents within shell scripts to be indented in a natural fashion.

Because you're using the unquoted version, you want to double up the backslashes that you want the Tcl interpreter to see. In your case, that's just the ones in the regular expressions. Thus, you want this (with everything above and below unchanged, I think):

    expect {
        timeout { exit 1 }
        -re {\\m\\d{1,}(\\.\\d{1,}){3}\\M}
    }
    if { ! [regexp {192\\.[0-9]{1,3}\\.{2}[0-9]{1,3}} $expect_out(0,string)]} {
        send -h "[COMMAND B]\\r" ;
    }

Oh, and you also need \\r (as above) throughout your script instead of \r.


I think it would be easier to split your script into two files, one which is just Bash code and the other which is just Tcl code, at least while you're developing it. Then you'd be able to make things work without having to fuss around with many layers of quoting. (The things you're substituting in now could instead be passed over as arguments to the script.)

When packaging everything back together, bash's printf %q may be helpful. It'll generate something hard to read, but it is a packaging operation after all…


And in relation to the error codes? The problem there is simpler; the test itself — [[ $? -eq 0 ]] — sets the error code. You have to save it to a proper variable and then test against that.

Here, check these boiled down cases:

bash-3.2$ ( exit 2 ); if [[ $? -eq 0 ]]; then echo ok; elif [[ $? -eq 2 ]]; then echo good; else echo bad; fi
bad
bash-3.2$ ( exit 2 ); echo $?; if [[ $? -eq 0 ]]; then echo ok; elif [[ $? -eq 2 ]]; then echo good; else echo bad; fi
2
ok
bash-3.2$ ( exit 2 ); code=$?; echo $code; if [[ $code -eq 0 ]]; then echo ok; elif [[ $code -eq 2 ]]; then echo good; else echo bad; fi
2
good

The first one is approximately what you're doing right now, the second shows just how non-obvious it can get — the intervening echo changes the result — and the third shows how to deal with it (storing the value in code here, but the name isn't really all that special).

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

insserv: Script is broken

broken image symbol (PHP script)

Copy and pasting renders script to be broken?

Shell script keeps making broken symbolic links

Bash Script for Concatenating Broken Dashed Words

Fixing broken umlauts from database in ruby script

Script broken after for-done ended

My Mac network location script is broken

Broken script when added to headers in WordPress

Multithreaded Perl script leads to broken pipe if called as a Python subprocess

My shell script to symlink a file from one directory to another is broken

Ubuntu 18.04 gnome-session broken post installation script

Piping to head results in broken pipe in shell script called from python

Is signing of shell script wrapping applications broken in OS X 10.11?

Asterisk* Originating a playback php script , but getting fwrite() broken pipe

Find Broken Reference path in script task C# SSIS

Shell script file existence test fails for broken symbolic link

.htaccess rewrite image file to php script -> show a broken image link

JavaScript functionality broken, once moved from in-line script to a separate file

broken link when using <a href=""> to link to other html file in google apps script

How do I make my Python script that is running on the server, to automatically restart after the connection is broken?

Script is broken after 1m, status failed, PHP-Apache when sending email with AWS SES

Editor script has broken sliders after passing in the min/max values for the sliders

Broken raspberry pi 4 case python3 script causes disk errors

Automatically logged in user runs broken script at login, advice on how to regain access to pc?

Calling Groovy script as a pre-commit hook on my subrepository using smartgit broken

Google App Script - Send long email from google sheet - email broken into lines

Running a script and get it alive after a SSH disconnection (like a broken pipe) without forcing it in background

Is sscanf broken?