Curly brackets in Expect language "expect" command? - tcl

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.)

Related

Expect programming:How to expect a particular string

I have logged in the CUCM then we are trying to execute one command which is coming correct, but by the Expected value we are not able to execute the second command.
first we are doing like this:-
send -i $install_id "utils ctl set-cluster mixed-mode\r\n"
puts "we are hitting yes &&&&&&&&&&"
expect {
-i $install_id -re ".*" {
send -i $install_id "y\r"
puts "$$$$$$$$$$$$$$$ we are inside...."
}
}
puts "we are done %%%%%%%%%%"
return 1
here first command send -i $install_id "utils ctl set-cluster mixed-mode\r\n" is getting executed sucessfully , but it is not going inside the expect loop.
the output of first command is something like:-
admin:utils ctl set-cluster mixed-mode
This operation will set the cluster to Mixed mode. Do you want to continue? (y/n):
after this the cursor will be in next line where i have to give y and enter, the output statment inside expect is also not getting printed
I'd be doing something like:
expect {
-i $install_id
-ex "continue? (y/n):" {
puts "I FOUND A CONTINUE PROMPT"
send -i $install_id "y\r"
exp_continue
}
"$thePrompt" {
puts "I FOUND A SYSTEM PROMPT"
}
}
Like that, it would only stop that expect once the command has finished, but it will respond to the continue prompt and keep going (because exp_continue). In this case, the -ex option is suitable as that matches a literal string, a great convenience when the thing that you are looking for contains multiple regular expression metacharacters (?, ( and )).
And change "$thePrompt" for the right thing to actually match the prompt, of course.
Use autoinspect(1).
Start it, then run all your commands manually, inspect the script.exp file it created. It should have recorded the exact matched strings, in case there is some weird shell coloring going on which makes you missing your expected string but having no idea why.

Proper Error handling EXPECT / TCL

What is the best way to prevent sending commands to a dead process?
Sometimes my session gets terminated when it's supposed to be open so I end up sending commands and getting the error:
send: spawn id exp4 not open
I was trying to do something like
if [catch send "test\r"] {
puts "send error!"
}
but it seems like the query is true every pass.
that's the simplest example, but I have more complex "send / expects" where I use capture groups etc, so putting a catch around every "send / expect" or creating a function doesn't seem that useful.
can you wrap a catch around the entire program? What is the proper way to catch errors like these?
There's a FAQ written by the Expect author that addresses this: http://expect.sourceforge.net/FAQ.html#q64
Seems like you want something like
expect_before {
eof { respawning_the_process }
}
I'm sure there's some wrinkles to be ironed out (like what to do when the process is supposed to end)
The problem with this:
if [catch send "test\r"] {
is two-fold:
you did not put braces around the condition, so it's not getting evaluated at the right time.
you did not provide the right arguments to the catch command
You would want to write:
if {[catch {send "test\r"} output] != 0} {
This can be abstracted into a proc
proc send {args} {
set status [catch [list exp_send {*}$args] output]
# error handling if $status is non-zero
}
"exp_send" is a builtin alias for the expect "send" command, so it's safe to override "send" with a proc, minimizing the amount of code changes you need.

exp::winnt_debug parent namespace error

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.

How to handle tcl expect output

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).

How do you save and parse a command output in Expect?

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'.