Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Expect doesn't work as Expected

by keenlearner (Acolyte)
on Jul 07, 2012 at 18:21 UTC ( [id://980494]=perlquestion: print w/replies, xml ) Need Help??

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

Hello, Monks, I am trying to make the expect interact with "units" program. But it does not want to match anything, except qr/.*/ and the $exp->before prints nothing. If you want to test, the units can be obtained from http://www.gnu.org/software/units/
use Expect; $Expect::Debug = 3; # verbose debug output $Expect::Log_Stdout = 1; # show chatter for debugging $Expect::Exp_Internal =1; my $exp = Expect->spawn("units") or die "Cannot spawn unit: $!\n"; $exp->log_stdout(1); $exp->expect(15, [ qr/you have/i => sub { my $exp = shift; print "you have"; $exp->send("2 km"); exp_continue; } ], [ qr/you want/i , sub { my $exp = shift; print "you want"; $exp->send("m"); exp_continue; }], [ 'eof', sub { my $exp = shift; print "eof"; print +$exp->before(); } ], [ qr/.*/, sub { my $exp = shift; print $exp->error; print $exp->before(); } ] );
The units executed in the terminal will print these
user@dell:~> units 2526 units, 72 prefixes, 56 nonlinear units You have:
In the log it shows this
spawn id(20): Does `' match: pattern #1: -re `(?^i:you have)'? No. pattern #2: -re `(?^i:you want)'? No. pattern #3: -eof `'? No. pattern #4: -re `(?^:.*)'? YES!! Before match string: `' Match string: `' After match string: `' Matchlist: () Calling hook CODE(0x7fd8217449c0)... Returning from expect successfully. spawn id(20): accumulator: `' Closing spawn id(20).
Before match string: `' Why does it show `' ? what is that string ? And what is wrong with the code ? Thanks

Replies are listed 'Best First'.
Re: Expect doesn't work as Expected
by jethro (Monsignor) on Jul 07, 2012 at 21:38 UTC

    Your regexp .* is the biggest problem here. .* matches everything, even the empty string! The module goes into an endless loop always matching the empty string and never advancing because nothing is matched or rejected, so new match rounds start at the same place.

    If you want to see how it works, change .* to .+ in your original script. Then you will see how it works and you will see sensible values printed for "after match string" and "before match string"

    I could get your script to work (although endlessly) by adding \n to your ->send strings and disabling your .* catch all or nothing regexp:

    #!/usr/bin/env perl use Expect; #$Expect::Debug = 3; # verbose debug output #$Expect::Log_Stdout = 1; # show chatter for debugging #$Expect::Exp_Internal =1; my $exp = Expect->spawn("units") or die "Cannot spawn unit: $!\n"; $exp->log_stdout(1); $exp->expect(15, [ qr/you have/i => sub { my $exp = shift; #print "you have"; $exp->send("2 km\n"); exp_continue; } ], [ qr/you want/i , sub { my $exp = shift; #print "you want"; $exp->send("m\n"); exp_continue; }], [ 'eof', sub { my $exp = shift; print "eof"; print +$exp->before(); } ], [ qr/blabla.*/, sub { my $exp = shift; print $exp->error; print $exp->before(); exp_continue; } ] );

    I suspect you will never get an 'eof' because units runs in an endless loop. You have to send the eof or control-d character to units or simply close the connection when the regexp /you have/ matches a second time, i.e. something like this works:

    [ qr/you have/i => sub { my $exp = shift; if (++$counter<=1) { $exp->send("2 km\n"); exp_continue; } else { $exp->soft_close(); + } } ],

    PS: Please use 'use warnings;' and 'use strict;'. No modern perl script is complete without them

      Hi

      Thanks for your help, I added that .* after when I find it does not work, just would like to see if the Expect got any input from the terminal.

      I realize it's okay if I execute the perl code from terminal.

      But it does not work if I use modperl executed in apache. It will always return eof without any match. Any suggestion for this ?

      Thanks

        You should use $exp->log_file to log the debug output to a file instead of to STDOUT while running the script in apache. Look for differences in the log to what you see in the log when you execute it on the command line/terminal

        Getting eof probably means that the mod_perl version of the script doesn't get any output from the units script. Do you see the output maybe in your browser page (highly unlikely)?

        Very important: Check apache log files for any hints of what is happening

        Expect FAQ says this:

        Is it possible to use threads with Expect?

        Basically yes, with one restriction: you must spawn() your programs in the main thread and then pass the Expect objects to the handling threads. The reason is that spawn() uses fork(), and perlthrtut:

        "Thinking of mixing fork() and threads? Please lie down and wait until the feeling passes."

        If your apache uses threads instead of forks, this might be a problem. I'm not sure about apache, on linux it should probably use fork (if you do "ps -ef" on the command line, you should see multiple lines with "httpd" or "apache"). On windows it probably uses threads

        As a test you could change the execution of "units" to a simple "echo you have" and look at the log output. If that changes anything your apache seems to have problems executing units. But then you should have found out about this in the log files, so this test is just to make double sure it is not the units program that is the problem

        You could ask the mailing list mentioned in the documentation (expectperl-discuss) if they know whether expect is generally working or not working in mod_perl

Re: Expect doesn't work as Expected
by zentara (Archbishop) on Jul 09, 2012 at 18:00 UTC
    Hi, just as a curiosity, to see if I could run units thru some form of IPC, I tried to run it with IPC::Open3. I had some trouble with it going into a runaway condition, until I added the -t (terse) switch. I also had trouble with getting the full output from the pipe after each prompt. But anyways, here is a running version.
    #!/usr/bin/perl use warnings; use strict; use IPC::Open3; #interface to "units" calculator my $pid = open3(\*WRITE, \*READ, 0 , "units -t"); # terse mode #if \*ERROR is set to 0, STDERR is sent to STDOUT foreach ( '100 yards', '1 mile', '1 kilometer', '1 parsec', '1 foobar' +){ print WRITE "$_\n meters \n"; chomp(my $answer = <READ>); print "$_ = $answer meters\n"; } __END__

    OUTPUT:

    100 yards = 91.44 meters 1 mile = 1609.344 meters 1 kilometer = 1000 meters 1 parsec = 3.0856776e+16 meters 1 foobar = Unknown unit 'foobar' meters

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://980494]
Approved by ww
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (2)
As of 2024-04-26 03:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found