Expect

Expect
Original author(s) Don Libes
Stable release 5.45.1 / 15 August 2012
Written in Tcl
Operating system POSIX, Windows
License Public domain[1]
Website expect.nist.gov
This article is about the Unix tool. For a definition of the word "expect", see the Wiktionary entry expect.

Expect, an extension to the Tcl scripting language written by Don Libes, is a program to automate interactions with programs that expose a text terminal interface. Expect was originally written in 1990 for Unix systems, but is now also available for Microsoft Windows and other systems. It is used to automate control of interactive applications such as telnet, ftp, passwd, fsck, rlogin, tip, ssh, and others. Expect uses pseudo terminals (Unix) or emulates a console (Windows), starts the target program, and then communicates with it, just as a human would, via the terminal or console interface. Tk, another Tcl extension, can be used to provide a GUI.

Basics

Expect has regular expression pattern matching and general program capabilities, allowing simple scripts to intelligently control programs such as telnet, ftp, and ssh, all of which lack a programming language, macros, or any other program mechanism.

Usage

Expect serves as a "glue" to link existing utilities together. The general idea is to try to figure out how to make Expect utilize the system's existing tools rather than figure out how to solve a problem inside of Expect.

A key usage of Expect involves commercial software products. Many of these products provide some type of command-line interface, but these usually lack the power needed to write scripts. They were built to service the users administering the product, but the company often does not spend the resources to fully implement a robust scripting language. An Expect script can spawn a shell, look up environmental variables, perform some Unix commands to retrieve more information, and then enter into the product's command-line interface armed with the necessary information to achieve the user's goal. After looking up information inside the product's command-line interface, the script can make an intelligent decision about what action to take, if any.

Every time an Expect operation is completed, the results are stored in a local variable called $expect_out. This allows the script to harvest information to feed back to the user, and it also allows conditional behavior of what to send next based on the circumstances.

A common use of Expect is to set up a testing suite, whether it be for programs, utilities or embedded systems. DejaGnu is a testing suite written using Expect for use in testing. It has been used extensively for testing gcc and is very well suited to testing remote targets such as embedded development.

One can automate the generation of an expect script using a tool called 'autoexpect'. This tool observes your actions and generates an expect script using heuristics. Though generated code may be large and somewhat cryptic, one can always tweak the generated script to get the exact code.

# Assume $remote_server, $my_user_id, $my_password, and $my_command were read in earlier
# in the script.
# Open a telnet session to a remote server, and wait for a username prompt.
spawn telnet $remote_server
expect "username:"
# Send the username, and then wait for a password prompt.
send "$my_user_id\r"
expect "password:"
# Send the password, and then wait for a shell prompt.
send "$my_password\r"
expect "%"
# Send the prebuilt command, and then wait for another shell prompt.
send "$my_command\r"
expect "%"
# Capture the results of the command into a variable. This can be displayed, or written to disk.
set results $expect_out(buffer)
# Exit the telnet session, and wait for a special end-of-file character.
send "exit\r"
expect eof

Another example is a script that automates ftp:

# Set timeout parameter to a proper value.
# For example, the file size is indeed big and the network speed is really one problem, 
# you'd better set this parameter a value.
set timeout -1
# Open an ftp session to a remote server, and wait for a username prompt.
spawn ftp $remote_server
expect "username:"
# Send the username, and then wait for a password prompt.
send "$my_user_id\r"
expect "password:"
# Send the password, and then wait for an ftp prompt.
send "$my_password\r"
expect "ftp>"
# Switch to binary mode, and then wait for an ftp prompt.
send "bin\r"
expect "ftp>"
# Turn off prompting.
send "prompt\r"
expect "ftp>"
# Get all the files
send "mget *\r"
expect "ftp>"
# Exit the ftp session, and wait for a special end-of-file character.
send "bye\r"
expect eof

Below is an example that automates sftp (with password):

#!/usr/bin/env expect -f

# procedure to attempt connecting; result 0 if OK, 1 otherwise
proc connect {passw} {
  expect {
    "Password:" {
      send "$passw\r"
        expect {
          "sftp*" {
            return 0
          }
        }
    }
  }
  # timed out
  return 1
}

#read the input parameters
set user [lindex $argv 0]
set passw [lindex $argv 1]
set host [lindex $argv 2]
set location [lindex $argv 3]
set file1 [lindex $argv 4]
set file2 [lindex $argv 5]

#puts "Argument data:\n";
#puts "user: $user";
#puts "passw: $passw";
#puts "host: $host";
#puts "location: $location";
#puts "file1: $file1";
#puts "file2: $file2";
 
#check if all were provided
if { $user == "" || $passw == "" || $host == "" || $location == "" || $file1 == "" || $file2 == "" }  {
  puts "Usage: <user> <passw> <host> <location> <file1 to send> <file2 to send>\n"
  exit 1
}

#sftp to specified host and send the files
spawn sftp $user@$host
 
set rez [connect $passw]
if { $rez == 0 } {
  send "cd $location\r"
  set timeout -1
  send "put $file2\r"
  send "put $file1\r"
  send "ls -l\r"
  send "quit\r"
  expect eof
  exit 0
}
puts "\nError connecting to server: $host, user: $user and password: $passw!\n"
exit 1

it should be noted that using passwords as command-line arguments, like in this example, is a huge security hole, as any other user on the machine can read this password by running 'ps'. You can however, add code that will prompt you for your password. Rather than giving your password as an argument. This should be more secure. See the example below.

stty -echo
send_user -- "Enter Password: "
expect_user -re "(.*)\n"
send_user "\n"
stty echo
set PASS $expect_out(1,string)

another example of automated ssh login in user machine

#timeout is a predefined variable in expect which by default is set to 10 sec
#spawn_id is another default variable in expect. 
#It is good practice to close spawn_id handle created by spawn command
set timeout 60 
spawn ssh $user@machine
while {1} {
  expect {
 
    eof                          {break}
    "The authenticity of host"   {send "yes\r"}
    "password:"                  {send "$password\r"}
    "*\]"                        {send "exit\r"}
  }
}
wait
close $spawn_id

Opinion

Pros

Expect can be run at regular intervals through the use of cron to encapsulate system administration tasks. This works because Expect merely uses system administration tools already located on the host computer. No extra tools need to be learned. If the programmer has already learned Tcl, then migrating to Expect is a relatively easy transition. The same programming structures and syntax exist, but with additional features built in.

There is large support in the industry for using Expect for many in-house administration tasks. It is widely used by companies such as Silicon Graphics, IBM, HP, Sun, Xerox, Amdahl, Tektronix, AT&T, ComputerVision and the World Bank to run in-house automated testing for development projects, file transfers, account administration, and network testing.

As a Tcl extension, Expect uses the same syntax, which is more regular and uniform than languages such as bash, csh, and Perl.

Expect goes to great lengths to abstract away the differences between terminal behaviour on various platforms. Other programs similar to expect are usually lacking in this regard. Because it is a Tcl extension, the full facilities of Tcl are available for use in expect scripts. Additional packages and extensions for Tcl can easily be used to expand the features of the Expect script.

Cons

Expect scripts must be cautiously guarded when including credentials such as username/password combinations. Anyone with read access to the script also has access to the stored credentials.

Alternatives

Various projects implement Expect-like functionality in other languages, such as C#, Java, Perl, Python and Ruby. These are generally not exact clones of the original Expect, but the concepts tend to be very similar.

C#

Java

Perl

Python

Ruby

References

Further reading

External links