#!/usr/bin/perl # There are three new things in this example. First is the concept #of closing the process, second is the concept of using regular #expressions in match patterns, and third is the concept of grabbing #stuff that is in the process's buffer. This last part is like when you #telnet to a host, do ls, and want to know the name of the files returned: # # #somehost$ ls #thing #thing.gz #core #Mail #mail #somehost$ # # When you use the expect() function it is possible to grab the #data before the next patter looked for. in this case if you were #looking for "somehost$ " you could get the names of the files returned #in the ls. In the example below the output of the 'id' command is grabbed #for use by the program. # # What this example tries to do is log in to foreign hosts and #grab the user id on each host. # use Expect; # Optional debugging, explained later. # $Expect::Debug=1 # $Expect::Exp_Internal=1; # $Expect::Log_Stdout=0; # On by default. die "Usage: $0 host1 host2... hostn\n" unless defined $ARGV[0]; @hosts = @ARGV; # Ssh would be better, rlogin is used here because it is more familiar. foreach $host (@hosts) { $rlogin=Expect->spawn("rlogin $host"); $rlogin->expect(30,"ssword: ") || die "Never got password prompt on $host, ".$rlogin->exp_error()."\n"; # We got the prompt, so send a password. print $rlogin "H4yB3vis\r"; # Now we look for a shell prompt. Notice the use of the regular expression. # To use a regexp make the previous argument '-re'. # expect will match on the hostname followed by a %, >, or $ followed by # whitespace. Notice that the regular expression part is in single quotes. # This is so that perl won't interpret control characters such as # whitespace (\s) for us until it gets in to the pattern matching engine. # my prompt is "darkwing$ ". Yours is probably different. # The below expect() call will return 1 if it matches the closed by foreign # host string or 2 if it matches the regular expression. # undef is returned if nothing matches. $match=$rlogin->expect(30,"closed by foreign host","-re",$host.'[%>$]\s'); die "Dumped by server\n" if $match == 1; die "Never got shell prompt on $host, ".$rlogin->exp_error()."\n" unless $match; # Ok, so we have a shell prompt. let's give it the 'id' command. print $rlogin "id\r"; # Now, we know that because ther terminal will echo "id\r" back to us # we should grab it so it won't muck with our final output. # Also, on most terminals if you send a carriage return it will respond # with a carriage return-newline combination. So we do: $rlogin->expect(30,"id\r\n"); # which clears id out of the buffer. Note that I used \r\n in double # quotes instead of single quotes because I want perl to look for # a carriage return instead of the literal \r\n. If I had instead done # $rlogin->expect(30,'-re','id\r\n'); it would work because it gets # tossed in to a regular expression where \r and \n get evaluated. # Now when we use expect() to find the next prompt the output of the last # command will be in the buffer that holds output between expect() calls. # this output should be 'before' we match. # I'm going to define what prompt we're looking for ahead of time to # make the call to expect more readable. $prompt = $host.'[%>$]\s'; $rlogin->expect(30,'-re',$prompt); # Yet another way to find out if we had a successful match is to test for # an error. die "Never got prompt after sending 'id' command\n" if $rlogin->exp_error(); # And the output of id: print "id on $host: $rlogin->exp_before()"; # Now, we do a hard close here. We are positively done with rlogin and we # know that it won't be ending by itself. This would be unlike, say, # a command like "tail /etc/passwd" which will exit as soon as it is finished # sending us its output. For that sort of a command soft_close() would # suffice. # It is important that you close rlogin so that processes don't # pile up eating your system resources during further loops through # hosts. $rlogin->hard_close(); # next host }