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

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!

Replies are listed 'Best First'.
Re^3: Executing command from perl script with input/output
by zentara (Cardinal) on Aug 09, 2005 at 11:25 UTC
    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!

        Hi, I have run into that "terminal" problem with a few scripts before. What I end up doing is the following "trick". Instead of opening $cmd with IPC::Open3, open "/bin/sh", then print $cmd to it.
        my $pid=open3(\*WRITE, \*READ, \*ERROR, '/bin/bash'); print WRITE $cmd;
        That usually works, because you are simualting a terminal.

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