Controlling Another Program with Expect (Perl Cookbook, 2nd Edition)

## 15.13. Controlling Another Program with Expect

### 15.13.1. Problem

You want to automate interaction with a full-screen program that expects to have a terminal behind `STDIN` and `STDOUT`.

### 15.13.2. Solution

Use the Expect module from CPAN:

>     use Expect;

>     $command = Expect->spawn("program to run")
>         or die "Couldn't start program: $!\n";

>     # prevent the program's output from being shown on our STDOUT
>     $command->log_stdout(0);

>     # wait 10 seconds for "Password:" to appear
>     unless ($command->expect(10, "Password")) {
>         # timed out
>     }

>     # wait 20 seconds for something that matches /[lL]ogin: ?/
>     unless ($command->expect(20, -re => '[lL]ogin: ?')) {
>         # timed out
>     }

>     # wait forever for "invalid" to appear
>     unless ($command->expect(undef, "invalid")) {
>         # error occurred; the program probably went away
>     }

>     # send "Hello, world" and a carriage return to the program
>     print $command "Hello, world\r";

>     # if the program will terminate by itself, finish up with
>     $command->soft_close( );

>     # if the program must be explicitly killed, finish up with
>     $command->hard_close( );

### 15.13.3. Discussion

This module requires two other modules from CPAN: IO::Pty and IO::Stty. It sets up a pseudo-terminal to interact with programs that insist on talking to the terminal device driver. People often use this for talking to passwd to change passwords. telnet (Net::Telnet, described in Recipe 18.6, is probably more suitable and portable) and ftp are also programs that expect a real tty.

Start the program you want to run with `Expect->spawn`, passing a program name and arguments either in a single string or as a list. Expect starts the program and returns an object representing that program, or `undef` if the program couldn't be started.

To wait for the program to emit a particular string, use the `expect` method. Its first argument is the number of seconds to wait for the string, or `undef` to wait forever. To wait for a string, give that string as the second argument to `expect`. To wait for a regular expression, give "`-re`" as the second argument and a string containing the pattern as the third argument. You can give further strings or patterns to wait for:

>     $which = $command->expect(30, "invalid", "success", "error", "boom");
>     if ($which) {
>         # found one of those strings
>     }

In scalar context, `expect` returns the number of arguments it matched. In the previous example, `expect` would return 1 if the program emitted "`invalid`", 2 if it emitted "`success`", and so on. If none of the patterns or strings matches, `expect` returns false.

In list context, `expect` returns a five-element list. The first element is the number of the pattern or string that matched, which is the same as its return value in scalar context. The second argument is a string indicating why `expect` returned. If there were no error, the second argument would be `undef`. Possible errors are "`1:TIMEOUT`", "`2:EOF`", "`3:spawn` `id(...)died`", and "`4:...`". (See the Expect(3) manpage for the precise meaning of these messages.) The third argument of `expect`'s return list is the string matched. The fourth argument is text before the match, and the fifth argument is text after the match.

Sending input to the program you're controlling with Expect is as simple as using `print`. The only catch is that terminals, devices, and sockets all vary in what they send and expect as the line terminator—we've left the sanctuary of the C standard I/O library, so the behind-the-scenes conversion to "`\n`" isn't taking place. We recommend trying "`\r`" at first. If that doesn't work, try "`\n`" and "`\r\n`".

When you're finished with the spawned program, you have three options. One, you can continue with your main program, and the spawned program will be forcibly killed when the main program ends. This will accumulate processes, though. Two, if you know the spawned program will terminate normally either when it has finished sending you output or because you told it to stop—for example, telnet after you exit the remote shell—call the `soft_close` method. If the spawned program could continue forever, like tail -f, then use the `hard_close` method; this kills the spawned program.

### 15.13.4. See Also

The documentation for the Expect, IO::Pty, and IO::Stty modules from CPAN; Exploring Expect, by Don Libes (O'Reilly)

Copyright © 2003 O'Reilly & Associates. All rights reserved.