You want to automate interaction with a full-screen program that expects to have a terminal behind STDIN and STDOUT.
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();
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 using 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", "succes", "error", "boom"); if ($which) { # found one of those strings }
In scalar context, expect
returns the number of arguments it matched. In the example above, expect
would return 1 if the program emitted "invalid"
, 2 if it emitted "succes"
, 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, 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 will 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.
The documentation for the Expect, IO::Pty, and IO::Stty modules from CPAN; Exploring Expect, by Don Libes, O'Reilly & Associates (1995).
Copyright © 2001 O'Reilly & Associates. All rights reserved.