Examples of Expect usage
Expect comes with a bunch of examples that are indispensable and unique full-function tools in their own right:
This script reads a Tcl script and enters it into an interactive tclsh.
#! /bin/env tclsh package require Expect proc tclprompt {} { proc [lindex [info level 0] 0] {} { return {\n%\s$} } return {%\s*$} } set timeout -1 set fh [open [lindex $argv 0]] set script [split [read $fh][close $fh] \n] spawn tclsh set command {} foreach line $script { if {[set line [string trimright $line]] eq {}} { continue } else { append command $line\r } if {[info complete $command]} { expect -re [tclprompt] { send $command set command {} } } } expect -re [tclprompt]
Some interesting points:
Setting timeout to -1 allows expect to wait as long as necessary for the next prompt.
[tclprompt] uses a $ to anchor the prompt to the end of the output. Expect is not line-oriented, so this is not foolproof.
[tclprompt] is implemented as a command rather than a variable because the first prompt is not preceded by a newline character, but subsequent prompts are, and manipulating the proc one time is more succinct than putting an extra conditional into the foreach statement.
simply checking for "%" is less robust than checking for a newline followed by "%" followed by space followed by the end of output. It's generally a good idea to be as specific as possible when expecting a prompt.
Here is an event-driven version of the same thing (only tested on *nix):
#! /bin/env tclsh package require Expect proc input {fh {command {}}} { if {$command ne {} && [info complete $command]} { send $command after 0 [list prompt $fh] return } if {[eof $fh]} { set ::done 1 return } gets $fh line if {[set line [string trimright $line]] ne {}} { append command $line\r } after 0 [list input $fh $command] } proc prompt {fh} { expect -re [tclprompt] { after 0 [list input $fh] } } proc tclprompt {} { proc [lindex [info level 0] 0] {} { return {\n%\s$} } return {%\s*$} } spawn tclsh set timeout -1 set fh [open [lindex $argv 0]] after 0 prompt $fh vwait done
by Froggy
Oftentimes I just want to grab all output and do as I wish with it afterwards. Although this can be done using fileevent instead of expect (see Pipes vs Expect) it is a task well suited to demonstrate how expect grab everything from a shell command. In this example, I launch the bash shell with the [spawn] command, then I send commands to the bash shell with [exp_send], and finally I use [expect] to retrieve my results.
# load the Expect package into Tcl package require Expect spawn bash exp_send "ls -l\n" set accum {} expect { -regexp {..*} { set accum "${accum}$expect_out(0,string)" exp_continue } } puts $accum
proc kermconnect {} { global opts expect_out spawn_id telnetbase if {$opts(host) ne ""} { spawn kermit -Y -j $opts(host) [expr $telnetbase + $opts(port)] } else { # use default kermit settings spawn kermit } set try 0 expect { "C-Kermit>" { send c\r } timeout { if {$try == 0} { send "set prompt\r" incr try exp_continue } failed "finding C-Kermit prompt" } } } proc kermdisconnect {} { global expect_out spawn_id expect "Kermit>" { send "close\r"} expect { timeout {failed "close connection"} -re "Closing connection.*Kermit>" { send q\r; after 100 } } uplevel #0 catch {close} uplevel #0 catch {wait}
RJ Here's a simpler way to catch all of the output:
proc exec_it {command} { spawn -noecho $command log_user 0 expect eof return [string trimleft $expect_out(buffer) $command] }
[L1 ] is a SourceForge-hosted project to which Cisco contributes. Several of the Cisco-specific examples which appear there are Expect scripts.
LV: Here's a question from an expect developer:
#!/usr/bin/expect -- set timeout 30 spawn /usr/local/bin/scp -P 36000 user@ip:/data/myfile /data1 expect { password: { send "password\r" } "yes/no)?" { send "yes\r" set timeout -1 } timeout { exit } eof { exit } }
when scp executes normally, everything is ok. But when the host does not exist, or is not reachable, the script stops, waiting for the "password" prompt. Because the file to be copied is very large, the code needs to cancel the timeout. How would one get the result of scp so as to handle this properly? RJ Larry, made some changes to above - see if that does what you need. Also, if you only get the end of the file, try expanding the buffer with the match_max command right after the spawn.
lpenz I usually make a "match anything" mask that resets the timeout:
expect { "password:" { send "password\r" } "yes/no)?" { send "yes\r" set timeout -1 } timeout { exit } -re . { exp_continue } eof { exit } }
As scp prints the transfer progress, each char printed resets the timeout. The timeout is effectively an inactivity detector.
dcd: Probably not related directly to expect, but that's where I encountered it, the following exec failed:
exec cat myfile | nc -w 5 $opts(host) [expr $tcpipbase + $opts(port)]
with a broken pipe error when run from a script, but worked when entered interactively in expect. After many hours of hair-pulling, this fixed it:
exec cat myfile | /usr/bin/nc -w 5 $opts(host) [expr $tcpipbase + $opts(port)]
Note that kermit will emulate telnet, but when you need a raw tcpip connection, nc will do the job..