Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks, and Happy Holidays!

First off..thanks to any who try to help me. :) I do appreciate it.

The goal: To go out and query our 'lightwaves' (I am a UNIX admin, and lightwaves are stripped-down linux boxes connected to the serial ports of our servers, to give us a remote console)for server/port information. Hard to believe, as of now, we don't have records and we work in a VERY LARGE ENVIRONMENT.

The tools: Hopefully, Perl's Expect.pm.

The drawback: I do not know Expect; I have chosen Perl's Expect module because I am adequate in Perl (although by no means and expert) and am learning more.

The problem: The documentation I've read is like mud to me, which , I guess, is because I don't know Expect and have never played around with pty's, etc.

I can spawn a ssh process, and issue commands to it, but I'm stuck on figuring out how to capture the output.

My ask is two-fold: 1) Does anyone know of a good source to read about using Expect.pm?

2) Given the code snippet below, can anyone guide me on how to capture the output of the 'listdev' command I issued on the lightwave? (plycon1 ). That is the part where I'm running into trouble.

($ssh = Expect->spawn("ssh -l sysadmin $server")) || die "couldn't sp +awn ssh: $!"; unless ($ssh->expect(10,"password")) { die "never got username prompt on $server, " .ssh->exp_error() +,"\n"; } print $ssh "$password\r"; unless ($ssh->expect(10,"sysadmin>")) { die "never got sysadmin prompt on $server, " .ssh ->exp_error( +),"\n"; } print $ssh "listdev\r";
Thank you!

Replies are listed 'Best First'.
Re: Expect.pm question
by Bloodnok (Vicar) on Dec 22, 2008 at 21:56 UTC
    I know there's a heck of a lot to be learnt from rolling your own, but in such circumstances as you have here, IMO, you'd be better useing (once again, pun intended:-) Net::SSH since it already has covered the problem(s) of interaction with the remote shell...

    A user level that continues to overstate my experience :-))
Re: Expect.pm question
by almut (Canon) on Dec 22, 2008 at 23:08 UTC

    You could wait for the next prompt after having issued the listdev command, and then print out the accumulated buffer contents, accessible via before(). Something like

    ... print $ssh "listdev\n"; unless ($ssh->expect(10,"sysadmin>")) { die "listdev command never finished, " .$ssh->exp_error()."\n" +; } my $out = $ssh->before();
Re: Expect.pm question
by liverpole (Monsignor) on Dec 23, 2008 at 16:57 UTC
    Expect is very powerful, but often difficult to get right (at least for me).  One thing that helps to debug is to print a lot of output.  I also find myself referring to the man page for Perl/Expect:  man Expect, which is a partial answer to your first question.

    One trick I've recently started using is to send a unique pattern to the remote host, expect that pattern back, and then just parse all of the text surrounding it.

    Here's an example:

    use strict; use warnings; ############# ## Globals ## ############# my $sendex_index = 0; ################# ## Subroutines ## ################# # # sendex # # Inputs: $1 - the Expect object # $2 - (optional) a verbosity level, if greater than 1, # displays verbose results in the send_expect() subrou +tine. # $3 - (optional) a timeout in seconds # $4 - (optional) a command to send to the remote host # # Results: Sends the command (if any) to the remote host, and then # tries to send a unique string of the format "Okay<number> +.", # where <number> will start with '1', and increase on each +call # to this subroutine. # # Returned will be the same info hash as returned by the # call send_expect(). # sub sendex { my ($exp, $vflag, $timeout, $cmd) = @_; $timeout ||= 3; ++$sendex_index; ($cmd || "") and $exp->send("$cmd\n"); my $okay = "echo -n Okay; echo -n $sendex_index; echo '.'"; my $pat = "Okay${sendex_index}."; $vflag ||= 0; return send_expect($exp, $timeout, $okay, $pat, $vflag); } # # send_expect # # Inputs: $1 - the Expect object # $2 - a timeout in seconds # $3 - a command to send to the remote host # $4 - the expected pattern to match # $5 - (optional) a verbosity level, if greater than 1, # displays verbose results # # Results: Sends the command (if any), and attempts to send a unique # string of the format "Okay<number>.", where <number> will # start with '1', and increase on each call. # # Returned will be the same info hash as returned by the # call send_expect(). # sub send_expect { my ($exp, $timeout, $cmd, $pat, $vflag) = @_; $vflag ||= 0; if ($vflag > 1) { print "=" x 79, "\n"; print "Sending text .... '$cmd'\n"; } $exp->send("$cmd\n"); if ($vflag > 1) { print "Expected text ... '$pat'\n"; } my $origstr = $pat; my $b_regex = ($pat =~ s,^/(.+)/$,$1,)? 1: 0; my @expargs = $b_regex? (-re, $pat): ($pat); my $result = $exp->expect($timeout, @expargs); $result or die "sent '$cmd', did not get '$origstr'\n"; my $before = $exp->before; my $match = $exp->match; my $after = $exp->after; my $c_strip = sub { my ($s_str) = @_; $$s_str =~ s/\e\]0;//g; $$s_str =~ s/\e\[[^m]*m//g; }; $c_strip->(\$before); $c_strip->(\$match); $c_strip->(\$after); if ($vflag > 1) { print "Full response:\n"; print "${before}${match}${after}\n"; print "=" x 79, "\n\n"; } return { 'before' => $before, 'match' => $match, 'after' => $after + }; }

    Each time you call sendex(), it echos a different pattern on the remote host (although it does so by echoing pieces of the pattern, so that the call to expect() doesn't succeed too early!):

    Okay1 Okay2 Okay3 ...

    and as soon as it receives the expected pattern, it sends back a hash containing the 'before', 'match', and 'after' patterns, which you can then parse to suit your needs.


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: Expect.pm question
by hbm (Hermit) on Dec 23, 2008 at 14:09 UTC

    I'd setup authorized keys so you're not prompted for password, rather than putting the password in your script. Then you might not even need Expect:

    system("ssh sysadmin@$server listdev");