in reply to Executing command from perl script with input/output

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

Replies are listed 'Best First'.
Re^2: Executing command from perl script with input/output
by linuxfan (Beadle) on Aug 08, 2005 at 18:21 UTC
    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
        Zentara,

        Thanks four your reply. After adding your changes to my script, I noticed that READ and ERROR do not have any data at all. This is weird because soon after I execute the binary (from a command line) it prompts with a list of choices to choose from. My suspicion is that the binary only sends out data if its STDOUT is connected to a terminal. I am not sure if open3 provides any connection to a terminal..

        I tried the write at the beginning of the program (as above) but neither of $answer or $error was populated.

        The pipe method you mention is what I've been using until now. I wanted to add more error checking by examining the choices the program gives the user before writing data back to its STDIN.

        Thanks for your time!