ActiveState Expect for Windows provides the ability to automate interactive programs from within the Windows environment. For example, use Expect for Windows to telnet to a Unix client and run commands on the remote system. By driving the telnet session with Expect for Windows, you have the ability to fully automate the process.
ActiveState Expect for Windows includes the following features:
This document discusses how to use Expect for Windows to automate various programs. This document is divided into three parts:
This document should be used together with the Expect for Windows Reference, which lists all available Expect commands and options.
This section discusses how to write Tcl scripts to use Expect for Windows. The first part addresses using Expect with Tcl. Next, three use cases are provided to demonstrate how Expect for Windows can be used to automate the interaction between various remote Unix programs. Each use case includes code snippets to demonstrate the communication between Expect for Windows and the interacting Unix program. It should be noted that this sample code is incomplete and does not provide the adequate error checking recommended for successful automation.
Expect for Windows is implemented as a Tcl package (as opposed
to a stand-alone executable). Expect for Windows must be declared
in a Tcl script with the package require Expect
statement. For example:
#!/bin/sh # \ exec tclsh "$0" ${1+"$@"} package require Expect
Failure to declare the package require Expect
statement results in an error message when running the script on
the Windows platform.
Scenario:
Use Expect for Windows to automatically upload a completed software
build from a Windows machine to a Unix FTP server.
# 1. Set the ftp prompt using a regular expression. set ftp_prompt "ftp>?" # 2. Print to console. puts stdout "Connecting to $hostname to upload the latest build." # 3. Connect to the FTP server using the "spawn" command. spawn ftp $hostname # 4. Wait for a login prompt. expect -re "(Name|login|Login|Username).*:.*" { # 5. Login prompt received. Send user name to Unix server. exp_send "$username\r" exp_send_user "sent username\n" } eof { # 6. No login prompt received. Display an error. exp_send_user "could not connect\n" } # 7. Wait for a password prompt from the Unix server. expect "Password:" { # 8. Password prompt received. Send the password. exp_send "$password\r" } # 9. Wait for an FTP prompt. Enter FTP commands. expect -re $ftp_prompt { # 10. Change to the upload directory on the Unix server. exp_send "cd $target_directory\r" } expect -re $ftp_prompt { # 11. Change to the local directory you want to # upload from (Windows machine). exp_send "lcd $build_directory\r" } expect -re $ftp_prompt { # 12. Upload the file to the Unix server. exp_send "put $build_file\r" } expect -re $ftp_prompt { # 13. Close the FTP connection to the Unix server. exp_send "exit\r" }Description:
Scenario:
Use Expect for Windows to update a user's password on a Unix server
via a Windows machine.
# 1. Open a telnet session to the Unix server. spawn telnet $authserver # 2. Wait for a login prompt. expect -re ".*login: " { # 3. Send user login name. exp_send "$user\r" } expect "Password: " { # 4. Send the user's old password. exp_send "$oldpass\r" } expect -re ".*$ " { # 5. Run the passwd command on the Unix server. exp_send "passwd\r" } expect -re ".*password: " { # 6. Send the user's old password. exp_send "$oldpass\r" } expect "New password: " { # 7. Send the user's new password. exp_send "$newpass\r" } expect "Retype new password: " { # 8. Send the user's new password again. exp_send "$newpass\r" } # 9. Close the session. exp_send "exit\r" # 10. The Windows console displays a confirmation message. exp_send_user "Password for $user changed."Description:
passwd
command on the Unix
server.Scenario:
Use Expect for Windows to automatically login to a remote Unix
server from a Windows machine. Use the "ls" program to remotely
list the contents of a directory. The Remote Directory Lister
Tutorial demonstrates how to write a complete Tcl script using
Expect for Windows. See the Remote Directory
Lister Tutorial for more information.
The ActiveState Expect for Windows implementation differs from the original Expect written by Don Libes (at the National Institute of Standards and Technology (NIST)). These differences are due to Expect initially being written for the Unix platform. This section addresses the various command and behavior modifications in the Windows port. The last part lists the caveats you should consider when writing Expect scripts.
To review all available commands and options, see the Expect for Windows Reference for more information.
The following Expect commands and behaviors are modified for Windows usage:
Modified Commands:
-slave
option is
not supported.-break
flag is
not supported. Does not implement a parser for VT100 codes for
special keypress codes such as "arrow up", F1, Home, etc.-ignore
and
-trap
options are not supported.-environment
: Takes a list of name/value pairs in
the form that array get
would return.-directory
: Sets the starting directory.Modified Behaviors:
CreateFile("CONOUT$")
fails when asking for an
existing TTY channel, there is no recourse in the generic layer to
create one.) Only a single console window is allowed per process on
Windows. This affects exp_send_tty
and
expect_tty
, and is a prerequisite for enabling
exp_interact
.signal.h
is not supported, nor are
windows-specific ones such as CTRL_SHUTDOWN_EVENT
.
Also, exp_wait
is limited in the same way for signal
exits, as is the core's Tcl_WaitPid()
which it
uses.The following commands are not applicable to the Windows platform. When used, these commands only return errors.
exec
instead
.This section addresses those commands and behaviors that have yet to be implemented in Expect for Windows:
Unimplemented Commands:
Unimplemented Behaviors:
thread::transfer
. This is a
limitation of Tcl's channel API.The following caveats should be reviewed before writing Expect for Windows scripts.
ExtensionsExtensions may collide with Expect command names. For example,
send
is defined by Tk for an entirely different
purpose. This is why the commands listed have the
"exp_
" prefix. Use these extended command names to
ensure compatibility between Windows and Unix environments.
Expect takes a liberal view of scoping. In particular, variables
read by commands specific to the Expect program are sought first
from the local scope, and if not found, in the global scope. For
example, this obviates the need to place global
timeout
in every procedure that uses
expect. On the other hand, variables written are
always in the local scope (unless a "global" command has been
issued). The most common problem this causes is when
exp_spawn
is executed in a procedure. Outside the
procedure, spawn_id
no longer exists, so the spawned
process is no longer accessible simply because of scoping. Add a
global spawn_id
to such a procedure.
Terminal parameters can have a big effect on scripts. For example, if a script is written to look for echoing, it misbehaves if echoing is turned off. For this reason, Expect forces terminal parameters by default. Unfortunately, this can make things challenging for other programs. For example, the emacs shell wants to change the "usual" mappings: newlines get mapped to newlines instead of carriage-return newlines, and echoing is disabled. A user can use emacs to edit the input line. Unfortunately, Expect cannot possibly guess this. You can tell Expect not to override its default terminal parameters, but you must then be very careful when writing scripts for such environments. In the case of emacs, avoid depending upon things like echoing and end-of-line mappings.
Single and Multiple ArgumentsCommands that accept arguments braced into a single list (the
expect
variants) use
a heuristic to decide if the list is actually one argument or many.
The heuristic can fail only in the case when the list actually does
represent a single argument which has multiple embedded
\n
's with non-whitespace characters between them. This
may seem unlikely, however, the argument -nobrace
can
be used to force a single argument to be handled as a single
argument. This could conceivably be used with machine-generated
Expect code. Similarly, -brace
forces a single
argument to be handled as multiple patterns or actions.
This section addresses various tips for writing Expect scripts.
Tip 1: Use a shell command trick in your scripts to associate the file type on Unix systems.
Place the following commands at the beginning of your Expect for Windows scripts to run them in both Unix and Windows.
#!/bin/sh # \ exec tclsh "$0" ${1+"$@"}Description:
exec tclsh "$0" ${1+"$@"}
command launches the
tclsh interpreter to run the Expect for Windows script in
Unix.Tip 2: Debug the process by viewing the controlled console.
Place the following variable near the beginning of your Expect for Windows scripts to view program interaction on the Windows platform.
# This special variable used by the ActiveState Expect for Windows # port will enable actual viewing of the controlled console. # Otherwise it remains hidden (default). set exp::winnt_debug 1Description:
set exp::winnt_debug 1
variable opens the
controlled console and displays the interaction between the Expect
for Windows script and the Unix program.Tip 3: Use a regular expression to recognize shell prompts.
A common Expect usage challenge is determining which shell
prompt to expect on the interacting Unix system. Since command
prompts vary greatly among users and shells, automating Expect to
interact with a program like rlogin
can prove
difficult.
One solution is to use a regular expression to match a variety of prompt
types. Another solution is to use catch
to see whether
a particular user stored a regular expression describing their
prompt in the EXPECT_PROMPT
environment variable.
set prompt "(%|#|\\$) $" ;# default prompt catch {set prompt $env(EXPECT_PROMPT)} expect -re $promptDescription:
set prompt
"(%|#|\\$) $"
To learn more about regular expression syntax and usage, see the Regular Expressions Primer for more information.
Tip 4: Matching output
About Output:
If you use a pattern of the form X*, the * matches all output
received from the end of X to the last character received. This
sounds intuitive but can be confusing because the phrase "last
character received" can vary depending upon the speed of the
computer and the processing of I/O, both by the kernel and the
device driver.
In particular, humans tend to see program output arriving in large portions (atomically) when in reality most programs produce output one line at a time. Assuming this is the case, the *, in X*, may only match the end of the current line even though there seems to be more. This is because at the time of the match this included all received output. Expect does not know that further output is arriving unless your pattern specifically accounts for it.
Express the last few characters of a prompt:
Always express the last few characters of a prompt when writing
patterns as depending on line-oriented buffering is unwise.
Programs rarely reveal the type of buffering they do and system
indigestion can break-up output lines at seemingly random
places.
Use "timeout" and "eof" to determine last output:
If you are waiting for a pattern in the last output of a program
and the program emits something else instead, you cannot detect it
with the timeout
keyword. The reason is that
expect
does not timeout. Rather, expect gets an
eof
indication. Use the eof
indication
along with the timeout
keyword to be sure to located
the true last output.
Matching a two-lined pattern:
Newlines are usually converted to carriage returns and linefeed
sequences when output by the terminal driver. When matching a
two-lined pattern as follows:
printf("foo\nbar")
You may require a pattern constructed as follows:
"foo\r\nbar"
Tip 5: Matching user input
A similar translation occurs when reading from the user, via
expect_user
. In this case, when you press "Return",
the character is translated to a newline. If expect
then passes that character to a program which sets its terminal to
raw mode (like telnet), a problem results, as the program expects a
true return. (Note that some programs are forgiving as they
automatically translate newlines to returns, but most do not.)
Unfortunately, there is no way to determine if a program puts its
terminal into raw mode.
Rather than manually replacing newlines with returns, the
solution is to use the command stty raw
, which stops
the translation. Note that you can not get the cooked line-editing
features when stty raw
is used.
Use the Tcl Dev Kit's TclApp to seamlessly deploy your Expect for Windows programs on any platform without installing Tcl or otherwise configuring the system. The TclApp tool simplifies the process of distributing an Expect for Windows application by collecting all of the files needed to run a Tcl application -- such as Tcl scripts, Expect libraries, graphics and other data files, Tcl extensions, a Tcl interpreter, and the standard Tcl and Tk libraries -- into a single executable file. A user can then install the Expect for Windows file anywhere on their system and execute it without installing any other packages or reconfiguring their system.
To wrap Expect for Windows applications in TclApp, include the
Expect base kit (located under Tcl/bin/
). Use the
Expect Tk base kit if you require the Tk package in your
application.
You can include an Expect base kit in TclApp via the command
line or through the TclApp user interface. The Expect base kit
includes the dll injector.dll
which will be
automatically extracted at runtime to the currently working
directory of the wrapped base kit for the controlling of spawned
applications. If this is not able to be extracted (e.g. on a
read-only file system), and the injector.dll
does not
already exist on the system dll path, spawning will fail.
Command Line:
-executable
flag on the command line, for
example:tclapp.tcl -nologogui -executable {C:\Tcl\bin\base-expect-tk-win32-ix86.exe} -out {C:\Tcl\tkremotels.exe} -pkg BWidget -relativeto C:\Tcl\demos\Expect C:\Tcl\demos\Expect\tkremotels.tcl
TclApp GUI Interface:
For more information about wrapping Expect scripts with TclApp, refer to the "tkremotels.tpj" demo. See TclApp on ASPN for more information on wrapping Tcl applications with TclApp.