Expect:
The Firefighter's Friend
Cameron Laird
Most UNIX administrators are familiar with the Expect utility.
They generally know it as a way to automate password entry in FTP
clients or password-management system commands.
As crucial as its use can be in these applications, Expect has
many other capabilities that may be less well known. Moreover, Expect
is available for Windows. This article briefly surveys the range
of Expect uses, then concentrates on ways Expect solves one-time
emergencies. One major role that systems administrators play is
to repair the damage -- missing files, lost passwords, and so
on -- that arises in day-to-day information technology (IT)
operation. These operations don't have to be as tedious as
they seem. In many cases, Expect can turn hours of drudgery into
a short scripting session.
Tcl/Tk Plus More
The first essential to understand about Expect is that it's
a full-blown, general-purpose programming language. Yes, it's
a handy tool for automation of password management, but that's
only a showy side effect of its more general capabilities.
Expect is an "extension" of the Tcl/Tk high-level language.
That means it does everything Tcl/Tk does, and knows a few commands
more, in the broad categories of:
- Debugging
- Character-oriented dialogue management
- Pseudoterminal management
Pseudoterminals take responsibility for "funny" character
handling, as with password entry made invisible by manipulating
terminal characteristics. "Dialogue management" has to
do with pattern matching the "conversation" between a
user and an application: the user types something, the program responds,
the user types again, and so on. Expect defines a convenient language
for making calculations based on such dialogues. You can, for instance,
use it to automate a dial-up connection:
spawn tip modem
expect "connected"
send "ATDT$number\r"
expect "CONNECT"
...
This is the beginning of a working Expect script that dials $number
to establish a connection before continuing with its work. The send
line feeds the ATDT sequence to the modem. The two expect
commands shown here know how to wait until they receive the proper
prompts ("connected" and "CONNECT")
before proceeding. Administrators who've worked with modems know
how clumsy it can be to try to program such interactions without Expect.
Completeness
I'll emphasize a few more times the point that Expect is
a complete programming language, in much the way C or Java is. Many
of the people who use it most refer to the one serious book published
on the topic, Don Libes' Exploring Expect. Libes is
the computer scientist at the National Institute of Standards and
Technology who originally wrote Expect, and continues to manage
its maintenance and enhancement. His book is remarkably well constructed
and accurate, and has been a valuable reference to a generation
of administrators.
The problem is that the book's first edition is so comprehensive
that it has never been updated. Since its publication in 1994, Tcl
has acquired powerful capabilities to handle binary data, network
connections, Unicode characters, concurrent programming, clock arithmetic,
and much more. When someone tells you what Expect can do, he's
probably right; when he tells you that a task is beyond Expect,
though, be careful he's not talking about a version from a
decade ago.
The completeness of more recent releases makes Expect an excellent
general-purpose upgrade from the shell or Perl scripting you might
otherwise use for "fire fighting". Here's an example
from my own work: I recently had to repair a busy server that had
a corrupt C run-time library.
At least, that's what we tentatively concluded; we never
made the time to diagnose the problem completely. What we knew was
that the machine was in active networked use, continuously serving
several thousand customers. However, some configuration files periodically
became corrupt. Moreover, its development system was missing. There
was no way to do compilations or even reliably copy over binary
executables.
Our most urgent need was to scan the configuration files periodically,
detect corruptions, and launch processes to correct the damage.
The only facilities at hand appeared to be the login shell and such
UNIX "built-ins" as sed and awk.
Yuck. While clever programmers have taught sed to invert
numeric matrices and play checkers, I'm only expert enough
for simple text transformations. I've never figured out how
to use sed with Unicoded content. Worse, at least one of
the remedies I needed to manage involved resetting passwords. That's
hard to do without Expect, and it didn't look as though I could
generate Expect on the server, or copy it there.
Work Through Whatever Channel You Have
That problem has a solution, though. A well-equipped development
host could reach the server through the network, and that's
all Expect needs. While a conventional password automation with
Expect might look like:
spawn passwd $user
expect password:
send $password\r
expect password:
send $password\r
expect eof
my first step was to set up a "remote control" form for
the same sequence:
spawn telnet $server
expect login:
send root\r
expect Password:
send $root_password\r
# Now we're logged-in to $server.
expect $shell_prompt
send "passwd $user\r"
expect password:
send $password\r
expect password:
send $password\r
expect eof
My actual script encapsulated this in a proc, Tcl's "subroutine".
Moreover, we ssh rather than telnet, and you should, too. I've
simplified the code in this article slightly from the real-world source,
to highlight the principles most likely to apply universally.
At this point, I was using Expect on a networked host to manage
passwords on the server. I still had the challenge of running calculations
over the contents of the configuration files, with few local tools.
Expect met that challenge, too, of course. I could see
the corruption perfectly well by logging in to the server and editing
the files. All that was left was to teach Expect to do the same.
I quickly threw together a small framework for "remote reads".
With just a bit more "plumbing", an ambitious programmer
could wrap this up as a low-performance network file system --
one that extends Tcl's conventional input-output facilities
to operate on any file, not just local ones. The heart of the program
is this:
# Turn off screen echoes.
log_user 0
# Log in through telnet.
spawn telnet $host
expect login:
send $user\r
expect Password:
send $pass\r
expect $prompt
# Just echo the contents into Expect's "front end".
send "cat $filename\r"
# I could also use a timeout in place of detection of $prompt.
expect {
^$prompt {
}
-re "^(\[^\r]*)\r\n" {
puts "The line is '$expect_out(1,string)'."
exp_continue
}
}
By itself, this program simply connects to $host, then echoes the
contents of $filename to the screen. The value of this, though, is
that all the data are programmatically accessible. In the real application,
I filtered $expect_out(1,string) in various ways to detect
corruptions. By bringing all the calculations into Expect, I could
conveniently process the suspect files one line at a time from a remote
host, while retaining the power to manipulate passwords and do other
sensitive operations.
Other languages and tools can do similar work. I don't know
of any other that make it so easy, though. If I were working with
any other language, it probably wouldn't have been worth my
time to set up the automations I did. We likely would have reinstalled
a development system, and worked directly on the host. As sensible
as that sounds, it would have taken a bit longer just to install
a working development system than the actual time to script and
test everything I needed with Expect acting as a remote controller.
Not Just for Breakdowns
Expect is great for this kind of "fire fighting". If
a system has enough functionality that you can do what you want
"manually", then Expect can "glue" together
the pieces to automate the operation.
It's not just broken systems that need this help, however.
Or, perhaps more precisely, Expect is also handy with systems designed
with broken interfaces. All kinds of operations "off the desktop"
are candidates for Expect programming:
- Modem control
- Real-time databases and physical control devices hosted on
special-purpose industrial operating systems
- Network nodes with serial-line connections
- Testbeds for operating-system kernel experiments
As systems administrators, we frequently deal with products whose
interfaces are "intuitive" and easy enough for demonstration
purposes. To configure one dimension of an embedded processor might
take only 30 seconds using the vendor's interface.
What do you do, however, when you have to assign values to 400
different "points" on 80 hosts? Expect allows you to escape
the tedium of manually entering values. You can script an interaction,
save yourself time, and achieve perfectly reproducible and auditable
results.
Notice that reproducibility is a natural qualification for testing
configurations. While I haven't experienced it in a "one-off"
or emergency situation, Expect is a great facility for setting up
all kinds of quality assurance (QA) programs. Systems administrators,
for example, can script regression tests that validate host configuration.
Expect is well known in QA circles; the DejaGnu test system on which
gcc relies, for example, is an Expect application.
Limits to Expect
There are limits to Expect's universality. Once people understand
how potent Expect is, they naturally want to apply it to all
problems. It simply doesn't help in a few areas, though. For
example, while Expect has contributed enormous value in testing
character-oriented programs, it brings nothing direct to QA of arbitrary
graphical user interface (GUI) applications.
Also, Expect occasionally is a heavy hammer for situations that
call out for a screwdriver. Expect enthusiasts sometimes try to
script email automations, or Web scraping, or FTP operations, through
use of such common UNIX command-line utilities as mutt, lynx,
or the standard ftp client. If this works for you, so much
the better; my own judgment, though, is that there are far more
convenient ways to achieve the same results. In the case of ftp,
for example, I've collected examples (http://starbase.neosoft.com/~claird/comp.unix/programmer/ftp_automation.html)
of automations that are generally easier to understand and maintain
than those that involve Expect-scripted ftp.
One final, little-known aspect of Expect is its GUI potential.
Although Expect cannot control external GUIs in the way it controls
external character-oriented processes, Expect is a great way to
build new GUIs. Suppose, for example, that you have written in Expect
a little automation to reassign a network switch. You might wrap
it up as a proc with this sort of signature:
proc set_switch {switch_name subnet_assignment} {
# ...
}
While you invoke set_switch from within Expect, you'll
be out of the office next week, and you want to leave something in
your co-workers' hands that they can use safely. All you have
to do is write:
package require Tk
package require Expect
proc make_labeled_entry {label variable} {
frame .$variable
label .$variable.label -text $label -width 18
entry .$variable.entry -textvariable $variable
pack .$variable -side top
pack .$variable.label .$variable.entry -side left
}
make_labeled_entry "Switch name" switch_name
make_labeled_entry "Subnet assignment" subnet_assignment
label .label
button .button -text "Update switch_name" -command {
set_switch $switch_name $subnet_assignment
.label configure -text "$subnet_assignment assigned to $switch_name."
}
pack .label .button -side top
When run, this script yields the small GUI control panel seen in Figure
1. Colleagues with point-and-click skills are likely to find this
more comfortable than Expect prompting, and the only cost to you is
fewer than 20 lines of source code.
Summary
When you have fires to fight -- small, one-time jobs too tedious
to welcome, but too important to neglect -- let Expect help
you. Expect has all the convenience of conventional shell scripting,
but gives networking, pseudoterminal management, Unicode, and GUI
capabilities for free. As Don Libes once observed, "Expect
is, after all, a tool made for dealing with crappy interfaces."
Judge for yourself how much of your daily job fits that description.
Learn more about Expect, including tutorials, the latest sources,
and so on, at:
http://starbase.neosoft.com/~claird/comp.lang.tcl/expect.html
My thanks to Andreas Kupries for his help in preparation of this article.
Cameron Laird is Vice President of Phaseit, Inc. (http://www.phaseit.net),
where he frequently consults on systems administration issues. He
stopped counting after the first hundred hours Expect saved from
his own work days. Cameron welcomes correspondence to claird@phaseit.net.
|