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

Hello Monks,
I need to execute a binary from my perl script. This binary writes out a few lines to STDOUT (as a question) and then prompts me to enter my choice (0 or 1). I am able to send data to the program using the following code:
sub ExecCmd { my ($cmd,$pipe) = @_; my @result; if(defined $pipe) { LogInfo("Executing $cmd"); open PIPE,"| $cmd" or die "Cannot fork command: $!"; local $SIG{PIPE} = sub { die "Failed to execute $cmd" }; print PIPE "0\n"; close PIPE; unshift @result,"PASS"; return \@result; } }
Now, I also want to be able to read the data that the binary writes to STDOUT as I want to verify that it is printing the right questions to STDOUT. I have looked at the Expect module and think it can help me do what I want. However, I am looking for a simpler method (if possible). Can you holy monks show me the way?

Thanks a lot!

Replies are listed 'Best First'.
Re: Executing command from perl script with input/output
by bart (Canon) on Aug 08, 2005 at 17:30 UTC
    I think you want to take a good look at IPC::Open2 and IPC::Open3, both standard modules (they come with perl). The difference between the two is is that the latter allows you to capture STDERR, too.
Re: Executing command from perl script with input/output
by zentara (Cardinal) on Aug 08, 2005 at 18:03 UTC
    Here is about the simplest example you can get using IPC::Open3. Of course a real world script may need alot more code. You can search here for other more complex examples, or search for them at groups.google.com
    #!/usr/bin/perl use warnings; use strict; use IPC::Open3; #interface to "bc" calculator #my $pid = open3(\*WRITE, \*READ, \*ERROR,"bc"); my $pid = open3(\*WRITE, \*READ,0,"bc"); #if \*ERROR is false, STDERR is sent to STDOUT while(1){ print "Enter expression for bc, i.e. 2 + 2\n"; chomp(my $query = <STDIN>); #send query to bc print WRITE "$query\n"; #give bc time to output select(undef,undef,undef,.5); #get the answer from bc chomp(my $answer = <READ>); print "$query = $answer\n"; } waitpid($pid, 1); # It is important to waitpid on your child process, # otherwise zombies could be created.

    I'm not really a human, but I play one on earth. flash japh
      Thanks for your reply. I found some sample code on the web that uses IPC::Open3 and used it in my program. Is it requirs to write data to the WRITE descriptor before I can read from it? My binary writes data to STDOUT/STDERR first, and then reads STDIN.
      sub ExecCmd { my $cmd = shift; my $pid = open3(\*WRITE,\*READ,\*ERROR,$cmd); unless (defined $pid) { LogError("Failed to execute $cmd"); return undef; } LogInfo("PID is $pid"); my $select = new IO::Select(); $select->add(\*READ); $select->add(\*ERROR); foreach my $handle ($select->can_read) { LogInfo("handle is $handle"); my $buf = ""; if($handle eq \*ERROR) { sysread(ERROR,$buf,BUFFER); close ERROR; if($buf) { LogInfo("ERROR -> $buf"); } } else { sysread(READ,$buf,BUFFER); close READ; if($buf) { LogInfo("READ -> $buf"); } } } print WRITE "0\n";
      In the above, the program seems to be waiting for some kind of input after it prints the PID - never enters the foreach loop. How can I solve the above problem?

      Thanks!

        Try putting the "print WRITE "0\n" before the select statements. I havn't tested your sub, but I have a feeling those "ifs" are not working, but I'm guessing. You also may need to insert a delay to let the program startup and return.

        Try this:

        sub ExecCmd { my $cmd = shift; my $pid = open3(\*WRITE,\*READ,\*ERROR,$cmd); unless (defined $pid) { LogError("Failed to execute $cmd"); return undef; } LogInfo("PID is $pid"); select(undef,undef,undef, 1); a little delay you can try #print WRITE "0\n"; #try it here too my $selread = new IO::Select(); my $selerror = new IO::Select(); $selread->add(\*READ); $selerror->add(\*ERROR); # may not be best use of IO::Select my($error,$answer)=('',''); #see which filehandles have output if($selread->can_read(0)){print "ready->read\n"} if($selerror->can_read(0)){print "ready->error\n"} #get any output sysread(ERROR,$error,4096) if $selerror->can_read(0); if($error){print "ERROR-> $error\n"} sysread(READ,$answer,4096) if $selread->can_read(0); if($answer){print "Response = $answer\n"} ($error,$answer)=('',''); print WRITE "0\n";

        Also, if you don't care what the initial output of the program is, you can avoid IPC::Open3 altogether, and use a piped open. Like:

        my $pid = open( FH, "| $cmd") or warn "$!\n"; print FH "0\n"; #maybe need syswrite here

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