Using Tcl and Expect to automate repetitive jobs
Archive - Originally posted on "The Horse's Mouth" - 2009-10-24 08:42:13 - Graham EllisIf you're typing the same series of instructions into your computer time and time again, perhaps with predictable variations depending on the responses that you get, you should consider using Expect. Expect adds three extra major commands to the Tcl language:
• spawn to start a process
• send to send a (user input) to that process
• expect to tell the program what to expect back.
There's a huge power in the expect command - you can have it wait for an exact string, a pattern (matched with directory style 'globbing' or regular expressions), or the first of several matches if you like. You can even spawn multiple processes and wait for any of them to respond with a match, and you can set up various fallbacks using expect_before and expect_after commands which let you trap any error messages that you get back in a common format without having to specify them all through the question and answer sequence.
However ... that's getting a bit advanced for an introduction. So let's have a look at a simple piece of code, as written on last week's Tcl Programming Course:
puts "This is a Tcl program"
OK - I started with that just to prove that this really IS Tcl running ;-). Then the spawn - expect - send sequence:
log_user 0
spawn ssh -l username www.wellho.net
expect "sword: "
send "xyzyxzxxw\r"
expect "3.2$ "
send "tail -60 /home/wellho/logdir/filename.txt\r"
expect "3.2$ "
set store $expect_out(buffer)
send "exit\r"
Some notes on that:
1. You'll run the code at first WITHOUT the log_user command; on your initial run, you'll want to see the interaction as it happens, so that you can debug it / fix any places where it hangs or times out.
2. You don't need to describe the full string you're expecting - just the end of it. The word "password" ends with "sword". However, you MUST add the ": " on the end of it if that is how the prompt ends so that the password isn't sent too early!
3. The \r means "carriage return" which is the key you press on your keyboard to send data. You do NOT send a new line (\n) when you press enter!
4. The expect_out array contains a great deal of interesting data after you've run an expect command, including the text that's been received since your last expect. Save it, use it!
As you get data back in you expect command, via the expect_out array, you can analyze it in Tcl. In the example above, where we saved the total response into a variable called store, we concluded the program with the following analysis:
set counter ""
foreach line [split $store \n] {
set lastmin [lindex $line [expr [llength $line] -3]]
regsub "," $lastmin "" load
lappend counter $load }
puts $counter
And the result - because the log file that we read was a server log noting how busy our system was - is a list of loads every minute for the last hour:
0.21 0.18 0.19 0.27 0.30 0.23 0.12 0.62 0.44 0.17 0.36 0.13
0.17 0.13 0.19 0.29 0.31 0.42 0.58 0.47 0.30 0.22 0.30 0.45
0.33 0.58 0.71 0.33 0.25 0.48 0.22 0.40
The complete source of this example is available here