Subject: ANNOUNCE: Expect 1.04
From: [email-masked]
Date: 19 Mar 1998 19:31:59 GMT
Newsgroups: comp.lang.perl.announce,comp.lang.perl.modules

There is a new version of Expect for Perl which should be making its
way across CPAN at this very moment. For those of you who aren't superexcited
just hearing about it because you know how cool the first publicly released
version was (heh heh heh), Expect is a successor to chat2.pl and comm.pl
which MUCH more closely ressembles tcl's Expect. Expect is MUCH more portable
than its predecessors and includes all the same neat-o debugging features
of tcl's Expect plus a little. A full introduction to Expect and why it's so
keen is included below.

	New and (I think) cool features include the ability to match regular
expressions and literals within the same call, settable multiline matching,
soft and hard closing of handles, a FAQ, and a couple nice bugfixes. All this
is meant to make it easier to use.
	I've also taken the time to do a brief tutorial on how to use it, as
per a request, including some cool stuff like building an arbitrary TCP
client to automate your dirty work.
	WARNING: I changed expect() to match literals by default as well as
match multiple lines so ^ and $ match beginnings and endings of lines. This
will make your life easier in the long run and it makes Expect more like its
tcl brethren but it makes it NOT DIRECTLY COMPATIBLE WITH 1.01 (!). Read the
docs! I don't plan on changing anything this fundamental with the module in
the future but I did warn people that it was *alpha* software in 1.01.

Following is a copy of the tutorial introduction, followed by a FAQ and an
example that demonstrates how you can use it to automate a process.

	Please send any comments to me @ habit.com, where me is tex.

	Thanks,

	Austin

-----

        Why is this tool useful?

        Chances are if you are reading this you probably have already used
the fine Expect for tcl, and possibly even read Exploring Expect. You are
interested in learning how to accomplish the same things you've done in tcl
using perl, or perhaps are just totally irritated at tcl.

        Expect is a generic tool for talking to processes that normally
require user interaction. This might be running an ftp client to grab a file,
telnetting to a router to grab statistics or reset an interface. Or, as in the
case of a place I recently administered, to start up a secure webserver
without having to be physically at the machine to enter the super secret
password.
        Expect talks to processes through ptys. To it, a process is mostly
just a bidirectional file handle, much the same as a socket. In fact, it is
possible to take a filehandle you've already used and pass it off to expect to
interact with.
        Now, something you might say at this point is "well, but there are
tools that I can use to do that with for more common protocols like telnet and
ftp already, such as Net::Ftp and Net::Telnet. Why would I want to use your
tool?". This is true. You might never want to use it. However, there are a few
advantages Expect has over similar modules:

        1. A consistent interface. You don't have to remember the syntax for
the other tools.
        2. It is more intuitive (my opinion, of course) because you already
know how to use the clients you are familiar with. Once you learn how to
talk to a process using Expect you will have an easy time automating your
other tasks.
        3. It is more versatile. With Expect you can connect multiple
processes together, write to log files, talk to sockets, etc.
        4. Consistent debugging. Debugging, IMHO, is much easier in Expect
than in other tools because you have the ability to watch the interaction
take place, and it's really pretty easy to use.

        One serious disadvantage of Expect is that scripts generated using it
are generally non-portable. The way a client 'looks' is important to building
a script to talk to it. Interacting with a client on DG-UX may be very
different than the equivalent client on SunOS. Or, and ncftp would be a good
example of this, a client may be different between versions. Similarly, if
an administrator changes versions of a server it might send back different
prompts than what you are looking for. These are things you should be aware
of.

----

	Here is 'example 5.A' of the tutorial, which demonstrates
interaction with an rlogin process.

#!/usr/local/bin/perl

$RSH='/usr/bin/ssh';

$host_to_login_to=shift(@ARGV);

use Expect;

# Debugging anyone?
# $Expect::Log_Stdout=1;
# $Expect::Exp_Internal=1;
# $Expect::Debug=1;

print "Enter password: ";
$|=1;
# First we have to initialize STDIN in to an expect object.
$stdin=Expect->exp_init(\*STDIN);
# Now turn off echoing
$stdin->exp_stty('-echo');
# The easy way to do this is:
#$password=<STDIN>;
#chop $password;
# The somewhat harder way is to use $stdin->expect. This would look like:
#
 ($match_num,$error,$match,$before,$after)=$stdin->expect(undef,"\n");
 $password = $before;
# Turn echo back on
$stdin->exp_stty('echo');
# print that newline that wasn't echoed
print "\n";
$rsh=Expect->spawn($RSH,$host_to_login_to);

# Look for a password prompt.
$rsh->expect(30,'-re','word:\s$')||(die"Never got password prompt\n");

print $rsh "$password\r";

# Look for a prompt. Prompt can be # $ > or ] followed by a whitespace.
$prompt  = '[\]\$\>\#]\s$';

# Note the use of -re
$rsh->expect(30,'-re',$prompt)||(die "Never got prompt on host\n");

# Start top
print $rsh "exec top\r";

# We already have an inited handle for STDIN above which we can use.
# We don't just do $rsh->interact because it will go monkeying
# with tty settings by default (set it raw).
$stdin->manual_stty(1);
# Instead we use interconnect directly.

# The trick here is to make sure:
# 1. Everything listening to a handle is added to its
# listen group.
# 2. Only what you want is jabbering at STDOUT.
#
# In this case only $rsh will talk to STDOUT so we don't have to
# change any log_stdout() settings.
$stdin->set_group($rsh);
Expect::interconnect($stdin,$rsh);

-----

	Here's example 6 which demonstrates how you can interact with a socket
to talk to (in this case) a mail server.
#!/usr/bin/perl -w

use Expect;
use IO::Socket;

# Arg. 0 hostname of mail server
$mail_server=shift(@ARGV);
# Remaining args will be email addresses.
@addresses=@ARGV;
die "Usage: $0 mail_server address1 [address2 address3.. addressN]\n" unless
  @addresses;

# Connect to mail server. This is right out of perldoc IO::Socket.

$smtp_sock = IO::Socket::INET->new(PeerAddr => "$mail_server:smtp(25)");
die "Couldn't connect to $mail_server, $!" unless defined $smtp_sock;

# Turn the socket in to an expect object.
$smtp_session=Expect->exp_init($smtp_sock);
# By default Expect doesn't print out the output of an exp_inited item.
# Generally you don't want handles jabbering at you. In this case
# we might turn it on so we can watch what happens.
#$smtp_session->log_stdout(1);

# Watch debugging?
#$smtp_session->exp_internal(1);

# Ok, now let's see if the mail server wants to talk to us:
$smtp_session->expect(30,'-re','^220.*\n')||die "Bad response from server\n";

# Cool. Now let's introduce ourselves to the server.
# There are many other ways to gain the FQDN of this box. This is mine,
# and it's easy. This of course assumes you have uname and that -n returns
# your hostname.
$my_hostname = `uname -n`; chomp $my_hostname;

print $smtp_session "HELO $my_hostname\n";

# My server responds with a 250 + stuff. Presumably that's RFC compliant.
# Feel free to go look :-)
$smtp_session->expect(30,'-re','^250.*\n')||die "Bad response after HELO\n";

# Try sending mail.. I should probably use my username rather than user@
# but I'm too lazy.
print $smtp_session "MAIL FROM:<user\@$my_hostname>\n";
$smtp_session->expect(30,'-re','^250.*\n')||die "Bad response after FROM\n";

# Now to check each address...
foreach $address (@addresses) {
  print $smtp_session "RCPT TO:<$address>\n";
  # Now check the status...
  ($match_num,$error,$match)=$smtp_session->expect(30,'-re','^\d\d\d');
  die "Never got response back after trying RCPT to $address\n" if $error;
  $status = $match;
  # Read to the newline so the server will be ready for the next address.
  # If the server spit back something other than 250 we'll display the
  # Whole error.
  ($match_num,$error,$match)=$smtp_session->expect(30,'-re','.*\n');
  die "Server seems to have hung after trying address $address\n" if $error;
  if ($status == 250) {
    $status = "ok\n";
  } else {
    $status.=$match;
  }
  print "Status of address $address: $status";
}

# Be good citizens, send a quit.
print $smtp_session "QUIT\n";

# At which point it should die nicely.
$smtp_session->soft_close();
----

	Dassa all! Send me kudos/grief!

	Austin

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/   Now offering spam-free web-based newsreading