Monday, February 7, 2011

How can I capture the stdin and stdout of system command from a Perl script?

In the middle of a Perl script, there is a system command I want to execute. I have a string that contains the data that needs to be fed into stdin (the command only accepts input from stdin), and I need to capture the output written to stdout. I've looked at the various methods of executing system commands in Perl, and the open function seems to be what I need, except that it looks like I can only capture stdin or stdout, not both.

At the moment, it seems like my best solution is to use open, redirect stdout into a temporary file, and read from the file after the command finishes. Is there a better solution?

  • IPC::Open3 would probably do what you want. It can capture STDERR and STDOUT.

    http://search.cpan.org/~rgarcia/perl-5.10.0/lib/IPC/Open3.pm

    From mopoke
  • I think you want to take a look at IPC::Open2

  • There is a special perl command for it

    open2()
    

    More info can be found on: http://sunsite.ualberta.ca/Documentation/Misc/perl-5.6.1/lib/IPC/Open2.html

    Leon Timmermans : Dude, why do you post something that has already been posted?
    From X-Istence
  • Somewhere at the top of your script, include the line

    use IPC::Open2;
    

    That will include the necessary module, usually installed with most Perl distributions by default. (If you don't have it, you could install it using CPAN.) Then, instead of open, call:

    $pid = open2($cmd_out, $cmd_in, 'some cmd and args');
    

    You can send data to your command by sending it to $cmd_in and then read your command's output by reading from $cmd_out.

    If you also want to be able to read the command's stderr stream, you can use the IPC::Open3 module instead.

    Brian Phillips : To anyone reading this, you should make sure and not use bareword filehandles as this example does. See this doc: http://www.perlfoundation.org/perl5/index.cgi?bareword_uppercase_filehandles
    From benzado
  • I always do it this way if I'm only expecting a single line of output or want to split the result on something other than a newline:

    my $result = qx( command args 2>&1 );
    my $rc=$?;

    $rc >> 8 is the exit code of the called program.

    if ($rc != 0 ) {

    error();
    }

    If you want to deal with a multi-line response, get the result as an array:
    my @lines = qx( command args 2>&1 );

    foreach ( my $line ) (@lines) {

    if ( $line =~ /some pattern/ ) {

     do_something();
    

    }
    }

    From
  • The perlipc documentation covers many ways that you can do this, including IPC::Open2 and IPC::Open3.

  • IPC::Open2/3 are fine, but I've found that usually all I really need is IPC::Run3, which handles the simple cases really well with minimal complexity:

    use IPC::Run3;    # Exports run3() by default
    
    run3( \@cmd, \$in, \$out, \$err );
    

    The documentation compares IPC::Run3 to other alternatives. It's worth a read even if you don't decide to use it.

    From xdg
  • If you do not want to include extra packages, you can just do

    open(TMP,">tmpfile");
    print TMP  $tmpdata ;
    open(RES,"$yourcommand|");
    $res = "" ;
    while(<RES>){
    $res .= $_ ;
    }
    

    which is the contrary of what you suggested, but should work also.

    From stephanea
  • A very easy way to do this that I recently found is the IPC::Filter module. It lets you do the job extremely intuitively:

    $output = filter $input, 'somecmd', '--with', 'various=args', '--etc';
    

    Note how it invokes your command without going through the shell if you pass it a list. It also does a reasonable job of handling errors for common utilities. (On failure, it dies, using the text from STDERR as its error message; on success, STDERR is just discarded.)

    Of course, it’s not suitable for huge amounts of data since it provides no way of doing any streaming processing; also, the error handling might not be granular enough for your needs. But it makes the many simple cases really really simple.

  • window.location.href='/';

0 comments:

Post a Comment