Examples edit
A very simple example - ping an ip address:#!/bin/sh # The next line is executed by /bin/sh, but not tcl \ exec /homes/ashnoc01/noc/rbacon/tcl/ActiveTcl/bin/tclsh8.4 $0 ${1+"$@"} package require Expect spawn -noecho /usr/local/bin/nping -c 5 $argv log_user 0 expect { "\r" { puts -nonewline "$expect_out(buffer)" exp_continue } eof { puts -nonewline "$expect_out(buffer)" } } puts "AutoPing finished\n" exitOutput:
> AutoPing 62.188.74.130 PING 62.188.74.130: 64 data bytes EchoReply from 62.188.74.130: len=64 ttl=242 seq=0 time=81.157 ms. EchoReply from 62.188.74.130: len=64 ttl=242 seq=1 time=80.768 ms. EchoReply from 62.188.74.130: len=64 ttl=242 seq=2 time=80.709 ms. EchoReply from 62.188.74.130: len=64 ttl=242 seq=3 time=80.915 ms. EchoReply from 62.188.74.130: len=64 ttl=242 seq=4 time=80.940 ms. ----62.188.74.130 PING Statistics---- 5 transmitted, 5 received, 0.00% packet loss. round-trip (ms) min/avg/max = 80.709/80.898/81.157 var/sdev/skew/kurt = 0.030/0.174/0.323/1.311 AutoPing finished
Discussion edit
escargo 20 Sep 2005 - Let's see if I remember this correctly. A frequently made mistake in Expect is to spawn a new process in a procedure, and then not assign the returned process identifier to anything that gets it out of the proc local scope.Without the pid to pass to other Expect commands, they won't work as intended. It might also be possible to pass the wrong pid because of the overwriting (or not overwriting) the spawn_id.RJ 21 Sep 2005 - You remember correctly. An important point. An example of that:
proc connect_server {server} { spawn -noecho rlogin $server set sid($server) $spawn_id return $sid($server) } set sid(myserver) [connect_server myserver.org] set sid(otherserver) [connect_server otherserver.org expect $sid(myserver) "%" exp_send "ls -l\r" expect $sid(otherserver) "%" exp_send $sid(otherserver) "ls -l\r" expect $sid(myserver) "%" set output_from_myserver $expect_out(buffer) expect $sid(otherserver) "%" set output_from_otherserver $expect_out(buffer) ....do stuff with the server outputArrays of spawn_ids allow you to connect to multiple servers and hold those connections open indefinitely, switching at will.escargo - I note that connect_server uses an array, sid, that is not declared as global. Is this one of those cases where variable scoping in expect is different than Tcl? (That is, in this case, expect doesn't find the variable in the local scope so it automatically checks the global scope for it.)RJ - Nope - Expect does not globalize variables unless told to. It really didn't need to be an array in the proc, just a variable. The return sends it back. But yes, it could have been a global variable - thus negating the need for returning it in the proc. I'm generally very bad at polluting the global namespace, so I try not to when I put up something anyone will actually look at. ;)
That example of using Expect to drive ping is totally unmotivating. You might as well use exec, no? Expect's strength comes from taking control of interactive processes like passwd or telnet.RJ The example was a request in Ask, and it shall be given # 3, placed here to kill the second bird (an example of using spawn). BUT, suppose the "\r" branch tracked the successful ping ratio and the ping was endless. The data could be used in graphical widget to display server availability realtime. Expect's power is limited only by the programmer's creativity IMHO. I would say its power is best illustrated by its ability to make non-interactive processes interactive. Also, I would not be in the least bothered if someone were to replace, improve or add to the above examples.escargo - And I would say its power comes from making interactive processes noninteractive.Alternatively, one could say Expect's power comes from making remote (or local) interactive processes operate under locally programmed control.RJ - David, I'm not sure we disagree at all, and I'm not sure this is the place for this, but what the heck, you've been here longer than I. My job involves interacting with thousands of network devices, so automating remote logins is indeed what I expect from Expect. See Secure Expect. However, I have since discovered how valuable Expect with Tcl/Tk is in establishing sessions with devices/servers to run one to many commands on each, massaging/combining the collective output to save our techs the time spent doing this manually. In effect, it lets me tie a series of non-interactive commands on multiple nodes together - creating an interactive and yes, locally programmed interface. It could be said we agree, yes?escargo - I think my definition of interactive requires a human in the loop. If a program controls another program, espeically one that expects interactions (like a login process), that's not interactive to my way of thinking. Two programs interacting without a human mediating are not interactive.DL - Using your definition of interactive, I would then have to disagree with your earlier statement about Expect's power to make interactive processes noninteractive. Many of the examples that come with Expect demonstrate it being used to control interactive processes but still leave the user interacting, just more efficiently. For example, scripts such as [tkpasswd] and multixterm are clearly interactive programs, even using your definition. So I would say that Expect's power is to partially or fully automate interactive programs thereby allowing users to use them more efficiently. PS: [tkpasswd] is a good example of a purely-local use of expect. (multixterm can be used locally too although I invariably use it remotely.)
I could not get the code to work on debian Linux. Here is code which works for me.
!/usr/bin/expect -f proc connect_server {server} { puts "spawing $server \r" spawn rlogin $server #spawn -noecho rlogin $server set sid($server) $spawn_id expect "assword: " exp_send "password\r" puts "returning \r" return $sid($server) } set s1 "192.168.1.32" set s2 "192.168.1.32" set sid(myserver) [connect_server $s1] set sid(otherserver) [connect_server $s2] puts "expecting \r" set spawn_id $sid(myserver) exp_send "\r" expect "debian:~#" exp_send "ls -l\r" set output_from_myserver $expect_out(buffer) expect "debian:~#" set spawn_id $sid(otherserver) exp_send "\r" expect "debian:~#" exp_send "df -k \r" set output_from_otherserver $expect_out(buffer) expect "debian:~#" puts "*******************************************************output_from_myserver \r" puts "$output_from_myserver \r" puts "*******************************************************output_from_otherserver \r" puts "$output_from_otherserver \r" #sleep 10 interact exit
"Expect with multiple spawns" [1] and "Expect plays a crucial role in network management" [2] have examples of typical concurrency concepts achieved with Expect. The point is that, while elementary sequential Expect scripts can respond to each of several different managed processes in turn, more sophisticated use of Expect's event-orientation makes it possible for Expect to handle different processes in the order each one returns (partial) results.