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...]
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.
Comments