By Don Libes
Price: $34.95 USD
£24.95 GBP
Cover | Table of Contents | Colophon
fsck, the UNIX file system check program, can be run from a shell script only with the -y or -n options. The manual defines the -y option as follows:
"Assume a yes response to all questions asked by fsck; this should be used with extreme caution, as it is a free license to continue, even after severe problems are encountered."
-n option is safer, but almost uselessly so. This kind of interface is inexcusably bad, yet many programs have the same style. ftp, a file transfer program, has an option that disables interactive prompting so that it can be run from a script. But it provides no way to take alternative action should an error occur.fsck problem, providing all the interactive functionality non-interactively. Expect is not specifically designed for fsck and can handle errors from ftp as well.fsck and ftp illustrate a major limitation in the user interface offered by shells such as sh, csh, and others (which I will generically refer to as "the shell" from now on). The shell does not provide a way of reading output from and writing input to a program. This means the shell can run fsck, but only by missing out on some of its useful features. Some programs cannot be run at all from a shell script. For example, fsck, the UNIX file system check program, can be run from a shell script only with the -y or -n options. The manual defines the -y option as follows:
"Assume a yes response to all questions asked by fsck; this should be used with extreme caution, as it is a free license to continue, even after severe problems are encountered."
-n option is safer, but almost uselessly so. This kind of interface is inexcusably bad, yet many programs have the same style. ftp, a file transfer program, has an option that disables interactive prompting so that it can be run from a script. But it provides no way to take alternative action should an error occur.fsck problem, providing all the interactive functionality non-interactively. Expect is not specifically designed for fsck and can handle errors from ftp as well.fsck and ftp illustrate a major limitation in the user interface offered by shells such as sh, csh, and others (which I will generically refer to as "the shell" from now on). The shell does not provide a way of reading output from and writing input to a program. This means the shell can run fsck, but only by missing out on some of its useful features. Some programs cannot be run at all from a shell script. For example, passwd cannot be run without a user interactively supplying the input. Similar programs that cannot be automated in a shell script are telnet, crypt, su, rlogin, and gdb. A large number of application programs are written with the same fault of demanding user input.
set), control flow (if, while, foreach, etc.), and perform the usual math and string operations. Of course, UNIX programs can be called (exec). I will provide a quick introduction to the language in (p. 23).expect command is the heart of the Expect program. The expect command describes a list of patterns to watch for. Each pattern is followed by an action. If the pattern is found, the action is executed.welcome", "failed", or "busy", and then it evaluates one of the corresponding actions. The action associated with busy shows how multiple commands can be evaluated. The timeout keyword is a special pattern that matches if no other pattern matches in a certain amount of time.
expect {
"welcome" break
"failed" abort
timeout abort
"busy" {
puts "busy"
continue
}
}
spawn tip modem expect "connected" send "ATD$argv\r" # modem takes a while to connect set timeout 60 expect "CONNECT"
tip program so that the output of a modem can be read by expect and its input written by send. Once tip says it is connected, the modem is told to dial using the command ATD followed by the phone number. The phone number is retrieved from argv, which is a variable predefined to contain the original argument with which the script was called.expect will wait before giving up. At this point, the script waits for the call to complete. No matter what happens, expect terminates. If the call succeeds, the system detects that a user is connected and prompts with "login:".passwd.passwd is the command to change a password. The passwd program does not take the new password from the command line. Instead, it interactively prompts for it—twice. Here is what it looks like when run by a system administrator. (When run by users, the interaction is slightly more complex because they are prompted for their old passwords as well.)
# passwd libes
Changing password for libes on thunder.
New password:
Retype new password:
passwd makes a lot of sense. Here is an Expect script to do just that—automate passwd so that it can be called from a shell script.spawn passwd [lindex $argv 0] set password [lindex $argv 1] expect "password:" send "$password\r" expect "password:" send "$password\r" expect eof
passwd program with the username passed as an argument. The next line saves the password in a variable for convenience. As in shell scripts, variables do not have to be declared in advance.expect command looks for the pattern "password:". expect waits until the pattern is found before continuing.\r indicates a carriage-return. (Most of the usual C string conventions are supported.) There are two expect-send sequences because passwd asks the password to be typed twice as a spelling verification. There is no point to this in a non-interactive passwd, but the script has to do it because passwdfg, bg) to switch jobs. These cannot be used from shell scripts. Similarly, the shell running non-interactively does not deal with history and other features designed solely for interactive use. This presents a similar problem as with passwd earlier. Namely, it is impossible to construct shell scripts which test certain shell behavior. The result is that these aspects of the shell will inevitably not be rigorously tested. Using Expect, it is possible to drive the shell using its interactive job control features. A spawned shell thinks it is running interactively and handles job control as usual.interact command turns control of a process over to you so that you can type directly to the process instead of through send commands.fsck, the UNIX program I mentioned earlier, which checks file system consistency. fsck provides almost no way of answering questions in advance. About all you can say is "answer everything yes" or "answer everything no".fsck and then in a loop answering yes to one type of question and no to another. The \\ prevents the next character from being interpreted as a wildcard. In this example, the asterisk is a wild card but the question mark is not and matches a literal question mark.
while 1 {
expect {
eof {break}
"UNREF FILE*CLEAR\\?" {send "y\r"}
"BAD INODE*FIX\\?" {send "n\r"}
"\\? " {interact +}
}
}
interact command, which passes control back to you. Your keystrokes go directly to fsck. When done, you can exit or return control to the script, here triggered by pressing the plus key. If you return control to the script, automated processing continues where it left off.fsck can be run non-interactively only with very reduced functionality. It is barely programmable and yet it is the most critical of system administration tools. Many other tools have similarly deficient user interfaces. In fact, the large number of these is precisely what inspired the original development of Expect.interact command can be used to partially automate any program. Another popular use is for writing scripts that telnet through a number of hosts or front-ends, automatically handling protocols as encountered. When such a script finally reaches a point that you would like to take over, you can do so. For example, you could browse through remote library catalogs this way. Using Expect, scripts can make a number of different library systems seem like they are all connected rather than different and disconnected.interact command is just a shorthand for the simplest and most common types of filtering that can be done in interactions. It is possible to build arbitrarily sophisticated mechanisms using the tools in Expect.adb program, for instance, can crash a UNIX system with a slip of the finger or, more likely, inexperience. You can prevent this from happening by securely wrapping the adb program in a script. Not only does this increase the safety of your system, but your system administrators no longer all have to be masters of its intricacies.dump program is another program with an unlikable interface. For example, dump often guesses incorrectly about the length of a tape and will prompt for a new tape even if one is not needed. An Expect script can be used to respond to dump so that it can continue automatically. A script can answer just this question or any question from dump.
xterm and other applications that specifically provide automation support, but Expect cannot automate any arbitrary X application..cshrc and .profile can be very complex. Few programs have such flexibility. And that is just as well. Such programs each have their own language and it is daunting to master each new one.ftp that have a limited ad hoc language—such as permitted in the file .netrc—not very flexible but better than nothing. A lot of programs are designed like ftp—their languages are limited, not extensible, and different from one tool to the next.telnet which have no control language whatsoever. The bulk of all interactive programs are like ftp and telnet—severely lacking in programmability.fg and bg to switch between processes in a shell, Expect can also switch its attention. Of course, Expect does it a lot more quickly than you can. The result is that Expect can act as "glue" for programs that were never designed to operate with other programs. An amusing example is the original chess program distributed with V7 and BSD UNIX written by Ken Thompson. It was designed to interact with a user—it prompts for moves and echoes user input in algebraic notation. Unfortunately, it does not accept its own output as input. Even if it did, there is no way to pipe both inputs and outputs between two processes simultaneously from the shell.cron. By using Expect with cron, you can not only automate the interactions but also automate starting the process in the first place. For example, you might want to copy over files each night between two networked but otherwise unrelated systems. And you can be arbitrarily selective about choosing files. You might want to copy over only executables that are less than 1 Mb and were created on a Saturday unless the current load exceeds 2.5 and the previous transfer took less than 5 seconds, or some other such complicated set of rules. There are no ftp clients that have such complex front-ends. But you can write Expect scripts to make whatever decisions seem appropriate while driving ftp.cron process interactively contact you while it is in the background. For example, an ftp script may need a password to continue what it is doing. It can search the network for you and then ask you for it. Or you can have it look for a set of users. After getting the password, Expect will go back and use it to complete the task. By having Expect query for passwords, you do not need to embed them in scripts.cron is another common reason to use Expect. If the backup program needs another tape, an Expect script can tell it to go on (for example, if your tapes are physically longer than the backup program thinks), or it can, again, contact you for assistance.ftp. But if you have a different program on your system to do file transfer, that's fine. Expect can use it. Expect does not have a built-in mechanism for file transfer, remote login, rebooting your colleague's workstation, or a million other useful things. Instead, Expect uses whatever local utilities you already have. This means that you do not have to learn a new file transfer protocol or a new host-to-host security system. If you use ".rhosts", then that is what Expect will use when it does remote host operations. If you use Kerberos, then Expect will use that. And so on.cat or ls if you need them too.telnet, rlogin, tip, etc.), it can remotely contact other computers even while running on a UNIX computer. In this way, it is very common to use Expect scripts to control non-UNIX computers such as Lisp machines, PostScript printers, modems, pagers, etc.telnet. The World Bank uses it to automate file transfers and updates. IBM uses it as part of a tape backup production environment. HP uses it to automate queries to multiple heterogenous commercial online databases. Sun uses it to sweep across their in-house network testing computer security. Martin Marietta uses it to control and extract usage statistics from network routers. Tektronix uses it to test software simulations of test instruments. The National Cancer Institute uses it to administer accounts across multiple platforms. Cisco uses it for network control and testing. Xerox uses it for researcher collaboration. Motorola uses it to control and backup multiple commercial databases. Data General uses it for software quality engineering. The Baha'i World Centre uses it to automate and coordinate data collection and storage from different telephone exchange locations. Amdahl uses it to automatically retrieve stock prices. CenterLine Software uses it for software quality assurance. Encore Computer uses it to simulate 500 users logging into one system at the same time. AT&T uses it to copy files between internal hosts through a firewall to and from external Internet hosts. Sandia National Laboratories uses it to control unreliable processes that need to be watched constantly. Schlumberger uses it to manage a network environment including routers, repeaters, bridges, and terminal servers all from different manufacturers. ComputerVision built an automated testbed using it. This is just a fraction of some of the users and uses of Expect. The whole list is truly astonishing, and it continues to grow rapidly./usr/local/bin. If you cannot find Expect, ask your system administrator. If you do not have a system administrator, you can obtain Expect by following the instructions below. Expect requires no special permissions to install, nor does it have to be installed in any particular place. You can even try it out in your own directory.example directory of the Expect distribution. Ask your local system administrator where the distribution is.README file in the example directory contains a complete list as well as full explanations about each of the examples:
chess.exp | play one chess game against another |
dislocate | allow disconnection from and reconnection to background processes |
dvorak | emulate a Dvorak keyboard |
ftp-rfc | retrieve an RFC from the Internet via anonymous ftp |
kibitz | let several people control a program at the same time for remote assistance, group editing, etc. |
lpunlock | unhang a printer waiting for a lock |
mkpasswd | generate a good random password and optionally run passwd with it |
passmass | set a password on many machines simultaneously |
rftp | allow recursive get, put, and list from ftp |
rlogin-cwd | rlogin with the same current working directory |
rogue.exp | find a good game of rogue |
timed-read | limit the amount of time a read from the shell can take |
timed-run | limit the amount of time for which a program can run |
tkpasswd | change passwords in a GUI |
tknewsbiff | pop up a window (or play sounds, etc.) when news arrives in selected newsgroups |
tkterm | emulate a terminal in a Tk text widget |
unbuffer | disable output buffering that normally occurs when program output is redirected |
weather | retrieve a weather forecast from the Internet |
.netrc or << redirection in a shell script, have ftp retrieve a file. Make the script retry if the remote site is too busy but not if the file cannot be found.ftp to make it more flexible. Use Archie if you need help..rc files.example directory online and try out some of the examples. Rewrite one in your favorite language.if/then/else, while, and set. Expect extends the language with commands such as expect and interact.name to the string value "Don", the variable word to the value "foobar", and the variable pi to "3.14159".set name Don set word foobar set pi 3.14159
name is different than Name.name to the string value "Don", the variable word to the value "foobar", and the variable pi to "3.14159".set name Don set word foobar set pi 3.14159
name is different than Name.$). The following command sets the variable phrase to foobar by retrieving it from the variable word.set phrase $word
phrase2 to the string "word=foobar".set phrase2 word=$word
money to the value "$1000".set money \$1000
\t" is a tab and "\b" is a backspace. Most of the Standard C backslash conventions are supported including \ followed by one to three octal digits and \x followed by any number of hex digits. I will mention more of these escapes later.# stick control-Z in a variable set controlZ \032 # define control-C set controlC \x03 # define string with embedded control-Z and tab set oddword foo\032bar\tgorp
#" is a comment. It extends to the end of the line. You can think of "#" as a command whose arguments are discarded.set word1 foo; set word2 bar ;# two commands set word3 foo\;bar ;# one command
;#" sequence above is a common way of a tacking comments on the end of a line. The "set string1 "Hello world"
set string2 "A semicolon ; is in here"
$, \t, and \b still behave as before.set name "Sam" set age 17 set word2 "My name is $name; my age is $age;"
word2 is left with the value "My name is Sam; my age is 17;".Sam was quoted, while in the second command 17 was not quoted, even though neither contained blanks. When arguments do not have blanks, you do not have to quote them but I often do anyway—they are easier to read. However, I do not bother to quote numbers because quoted numbers simply look funny. Anyway, numbers never contain whitespace.pid command returns the process id of the current process. To evaluate a command and use its return value as an argument or inside an argument, embed the command in brackets. The bracketed command is replaced by its return value.set p "My pid is [pid]."
set command returns its second argument. The following command sets b and a to 0.set b "[set a 0]"
set b [set a 0] set b "[set a 0]" set b "[set a 0]hello world" set b [set a 0]hello
1+1" is treated as an expression, it has the value 2. Tcl does not automatically treat strings as expressions, but individual commands can. I will demonstrate this in a moment. For now, just remember that expressions are not complete commands in themselves.1 + 5 1+5 1+5.5 1e10+5.5 (5%3)*sin(4) 1 <= 2 (1 <= 2) || (2 != 3)
+, -, *, /, and % (modulo). Many functions exist such as sin, cos, log, and sqrt. Boolean operators include || (or), && (and), and ! (not). The usual comparison operators are available (<= (less than or equal), == (equal), != (not equal), etc.). They return 0 if the expression is false and 1 if it is true.0) and hexadecimal (any number with a leading 0x). Functions such as floor, ceil, and round convert from floating-point to integer notation.1-2*3" is interpreted as "1-(2*3)" because multiplication is of higher precedence than addition. All binary operators at the same level of precedence are left-associative. This means, for instance, that the expression "1-2-3" is interpreted as "(1-2)-3". Since Tcl expressions rarely become complex, I will omit a lengthy discussion of the numerous levels of precedence, and instead note that you can always use parentheses to override a particular precedence or associativity. See the Tcl reference material for the complete list of operators and their precedences.1 + $age $argc < 10
set var1 "a$b[set c]\r"
set var2 {a$b[set c]\r}
var1 contains an "a" followed by the values of b and c, terminated by a return character. The variable var2 contains the characters "a", "$", "b", "[", "s", "e", "t", " ", "c", "]", "\", and "r".while loops, for loops, and procedures. These commands need to see the strings without having $ substitutions and bracket evaluations made.while command loops until an expression is false. It looks very similar to a while in the C language. The following while loop computes the factorial of the number stored in the variable count.
set total 1
while {$count > 0} {
set total [expr $total * $count]
set count [expr $count−1]
}
set commands between the braces. The body is executed as long as $count is greater than 0.while command has two arguments. The first is the controlling expression. The second is the body. Notice that both arguments are enclosed in braces. That means that no $ substitutions or bracket evaluations are made. For instance, this while command literally gets the string "$count > 0" as its first argument. Similarly, for the body. So how does anything happen?while command itself evaluates the expression. If true (nonzero), the while command evaluates the body. The while command then re-evaluates the expression. As long as the expression keeps re-evaluating to a nonzero value, the while command keeps re-evaluating the body.set command. The set command does not do any evaluation of its second argument. Consider this command:
set count [expr $count−1]
[expr ...] part is eval