I get the error "can't set "::exp::winnt_debug": parent namespace doesn't exist" when I try to run my expect script using the C implementation of expect interpreter on Windows (expect543.dll).
However the same script works fine if I run it through the ActiveState command tclsh...
The statement "set ::exp::winnt_debug 1" in the script is the cause of the error.
Any idea what might be the reason and how to resolve it?
Please find the code below
package require Expect
set ::exp::winnt_debug 1
set prompt "R4#"
set more " --More--"
expect -timeout 10 "$prompt"
set output [open result.txt "w"]
set running 1
spawn plink -telnet "144.21.12.45" -P 2004
send "enable\r"
send "\r"
send "show running-config\r"
send "\r"
while { $running > 0 } {
expect {
"\n" { puts -nonewline $output "$expect_out(buffer)" }
"$more" {send " "}
"lines *-* " { send " " }
#"$prompt" { set running 0 }
eof { set running 0 }
timeout { set running 0 }
}
}
puts "output is .."
There may be several implementations of Expect for Windows (unlike the Unix version, which has been stable for ages) and it sounds like the details of how they are implemented internally varies quite a bit between them. That's not especially surprising. Furthermore, the variable ::exp::winnt_debug is absolutely internal to a particular implementation.
The immediate fix is to change the line with the error to this:
catch {set ::exp::winnt_debug 1}
Like that, if it fails, it fails silently and won't cause the rest of the program to not run. (Enabling debugging shouldn't make any difference to whether the code runs!)
More generally, either use the ActiveState build (and work out how to package things together in the right way, bearing mind that critical dependency) or stop referring to internal features of it. It's very bad form to be poking your fingers inside the implementation of a package, as nobody ever gave a commitment to support them.
Related
I am writing an expect script to do make (compile). It's a multi step script and I try to catch the error and stop the script if there is any.
Here is a sample:
set BLD_PASS 1
set BLD_PASS [catch {exp_send "make buildXYZ\r"} output]
expect {
folder] {
if { $BLD_PASS == "0" } {
send_user "Build passed on XYZ \n\n"
} else {
send_user "Build failed on XYZ\n\n"
exit
}
}
}
The problem is I always get BLD_PASS as 0 even when there is real failure.
As I see, the set command gets executed and the BLD_PASS gets the value immediately though the 'make' takes minutes to finish. So, it doesnt look like it's really getting the output of make.
I have verified 'make' outputting the right return code by using it in a bash script.
Could anyone please help me to solve this problem ? Appreciate your help.
Please let me know if you need more info.
The reason why your code isn't working is that the send itself is succeeding: it sent the message to the other end. It just didn't get the result back yet. It might be easiest to do this by asking the other side to report the exit status and then listen for that.
send "make buildXYZ; echo FROGBARGLE_\$?_BARGLEFROG\r"
expect {
-re {FROGBARGLE_([^\s_]+)_BARGLEFROG} {
puts "exit code was $expect_out(1,string)"
}
}
There are fancier ways of tracking this sort of thing (and FROGBARGLE_…_BARGLEFROG is perhaps a bit too arbitrary; I picked it to be something really unlikely to occur in make output) but this sort of thing in general is the way you do it when working remotely: you have to tell the other side to tell you what happened.
I am very new to Expect/TCL and just wanted to confirm that in the example below, the curly-braces just work as single-quotes in bash to group the body of the expect command? Example:
expect {
-re "(P|p)assword: " { send "$pwd\r" }
-re "Connection timed out" { puts "Timeout error"; exit 1 }
-re "Connection closed" { puts "Host error"; exit 1 }
timeout { puts "Timeout error"; exit 1 }
eof { puts "Connection error"; exit 1 }
}
In standard Tcl, that's exactly how braces work. (OK, lots of command implementations then use the quoted thing immediately, but everything works pretty much as you'd expect.)
However, you're looking at the body of an expect command. The outer braces (on the first and last line) are the ones that are guaranteed to work that way; the others are up to the expect command — implemented in C — to interpret as it chooses. The documentation states that if the contents of the braces are multiline, they're interpreted as a (Tcl) list, which makes them work in pretty much the way you're after. (As Tcl commands go, that's deeply strange, but Expect has worked that way for decades now so it isn't about to change.)
Is it possible to prevent call stack to be outputted when error occurred. So for example suppose:
set error [catch { [exec $interpName $tmpFileName] } err]
if { $error ne 0 } {
puts "err = $err" #<---- Here call stack is also outputted
}
So output now looks like:
error: some error message
while executing
[stack trace]
Tcl automatically builds up the call stack in the global variable errorInfo (and, since 8.5, in the -errorinfo member of the interpreter result options dictionary) but it is up to the calling code to decide what to do with it. The default behavior of tclsh is to print it out; other Tcl-hosting environments can do different things (it's usually recommended to print it out as it helps hunt down bugs; on the other hand, some programs — specifically Eggdrop — don't and it's a cause of much trouble when debugging scripts).
You take control of this for yourself by using catch in the script that's getting the original error. The easiest way to do this is to put the real code in a procedure (e.g., called main by analogy with C and C++) and then to use a little bit of driver code around the outside:
if {[catch {eval main $argv} msg]} {
puts "ERROR: $msg"
# What you're not doing at this point is:
# puts $errorInfo
exit 1
} else {
# Non-error case; adjust to taste
puts "OK: $msg"
exit 0
}
Note that in your code, this would go inside the script you write to $tmpFileName and not in the outer driver code that you showed (which is absolutely fine and needs no adjustment that I can think of).
I am writing an expect script to telnet to the router ,do some config,expect some output.
If the required prompt is not available then it waits for it and gets time out.
So how do i have to handle this and print an error msg.
set timeout 30;
puts "Telnet to $IP 2361\n\n";
spawn telnet $IP 2361;
expect ">";
send "ACT-USER::$user_name:1::$password;";
expect ">";
How do i handle and print an error msg if the expected value is not received?
Dealing with timeouts nicely requires a slightly more complex use of expect:
expect {
">" {
# Got it; don't need to do anything here because we run the code after
}
timeout {
send_user "timed out, oh no!\n"
exit 1
}
}
# Now we put the rest of the script...
send "ACT-USER....blah"
# ...
Note that I'm surprised that your send doesn't end in \r (to simulate pressing the Return key).
I am half-way through writing an Expect script on a Linux server which is supposed to telnet to a router in order to collect some system information. So far my script can successfully make the connection, run a router command, disconnect and terminate.
The command displays a few lines which I need to parse, something I am not sure how to do in Expect. How can I save the output, grep a line, then a column from the line, and finally save the result in a file? If possible, I would like to use Expect entirely rather than a work-around (for example Expect embdded in Bash).
Thanks for your time.
jk04
Two tips for expect development:
autoexpect to lay out a framework for your automation
exp_internal 1 to show verbosely what expect is doing internally. This one is indispensable when you can't figure out why your regular expression isn't capturing what you expect.
basically, $expect_out(buffer) [1]. holds the output from last expect match to the current one. you can find your command output there.
and for the string manipulation, you can simply employ the tcl's built-in [2][3].
"How to access the result of a remote command in Expect" http://wiki.tcl.tk/2958
"regexp" http://wiki.tcl.tk/986
"string match" http://wiki.tcl.tk/4385
I've faced and solved a similar problem for interacting with bash. I believe the approach generalizes to any other interactive environment that provides no-op, fixed-string output.
Basically, I wrap the command with two fixed strings and then search for the pattern that includes these strings at the beginning and end, and save the content in between them. For example:
set var "";
expect $prompt { send "echo PSTART; $command; echo PEND;\r"; }
expect {
-re PSTART\r\n(.*)PEND\r\n$prompt { set var [ string trim $expect_out(1,string) ]; send "\r"; }
-re $prompt { set var "" ; send "\r"; }
timeout { send_user "TIMEOUT\n"; exit }
}
I suspect that this approach would work with a shell's comment characters as well, or a simple status command that returns a known output.
Then you can do whatever you need with the content of 'var'.