http://qs321.pair.com?node_id=454715

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

I would like to run an external command, and capture the STDOUT, STDERR and the exit status of that command. This will run on *nix, so for example, I can do this:

my $status = system '/bin/ls / > /tmp/output 2>&1';
This will give me the exit status, and allow me to slurp the output. But I am wondering if anyone knows a way for me to do both at the same time. If only IPC::Open3 gave me a way to capture the exit status.



pbeckingham - typist, perishable vertebrate.

Replies are listed 'Best First'.
Re: Capturing both STDOUT, STDERR and exit status
by merlyn (Sage) on May 06, 2005 at 16:13 UTC
Re: Capturing both STDOUT, STDERR and exit status
by polettix (Vicar) on May 06, 2005 at 16:31 UTC
    A backtick/qx suffices to mimic what you already do:
    my $alloutput = qx(/bin/ls / 2>&1); my $exitcode = $? >> 8;
    If you need to keep stderr and stdout separate use merlyn's suggestion.

    Flavio (perl -e 'print(scalar(reverse("\nti.xittelop\@oivalf")))')

    Don't fool yourself.

      Thanks, this is nice and clean. I did make the qx output scalar, otherwise it gives me a list of output lines.

      sub executeCommand { my $command = join ' ', @_; ($? >> 8, $_ = qx{$command 2>&1}); } my ($status, $output) = executeCommand ('/bin/ls', '/');



      pbeckingham - typist, perishable vertebrate.
        You have to reverse the return list, otherwise you'll refer to the previous value of $?:
        sub executeCommand_wrong { my $command = join ' ', @_; ($? >> 8, $_ = qx{$command 2>&1}); } sub executeCommand_correct { my $command = join ' ', @_; ($_ = qx{$command 2>&1}, $? >> 8); } my $command = 'echo -n ciao ; false'; my ($status, $output) = executeCommand_wrong ($command); print "[$output] -> [$status]\n"; ($output, $status) = executeCommand_correct($command); print "[$output] -> [$status]\n"; __END__ [ciao] -> [0] [ciao] -> [1]
        If you cannot live without having $status as the first returned value, just use reverse:
        sub executeCommand { my $command = join ' ', @_; reverse ($_ = qx{$command 2>&1}, $? >> 8); }

        Flavio (perl -e 'print(scalar(reverse("\nti.xittelop\@oivalf")))')

        Don't fool yourself.
Re: Capturing both STDOUT, STDERR and exit status
by zentara (Archbishop) on May 06, 2005 at 16:40 UTC
    Heres an example. You can also use select on the filehandles.
    #!/usr/bin/perl use warnings; use strict; use IPC::Open3; my $cmd = 'ls -la'; my $pid = open3(\*WRITER, \*READER, \*ERROR, $cmd); #if \*ERROR is 0, stderr goes to stdout while( my $output = <READER> ) { print "output->$output"; } while( my $errout = <ERROR> ) { print "err->$errout"; } waitpid( $pid, 0 ) or die "$!\n"; my $retval = $?; print "retval-> $retval\n";

    I'm not really a human, but I play one on earth. flash japh
      (hopefully not being too much of a thread necrophile here) This is an excellent example, which has just saved me from abhorrent temp file hell. One small quibble; the retval line should probably read:
      my $retval = $? >> 8;
Re: Capturing both STDOUT, STDERR and exit status
by saintmike (Vicar) on May 10, 2005 at 17:54 UTC
    Sysadm::Install provides a function for that:
    use Sysadm::Install qw(tap); my($stdout, $stderr, $rc) = tap "/bin/ls", "-l", "foobar"; print "stdout=$stdout\n", "stderr=$stderr\n", "rc=$rc\n";