[ACCEPTED]-How can I read the error output of external commands in Perl?-backticks

Accepted answer
Score: 32

There's the answer in perlfaq8: How can I capture STDERR from an external command?


There are three basic 59 ways of running external commands:

system $cmd;        # using system()
$output = `$cmd`;       # using backticks (``)
open (PIPE, "cmd |");   # using open()

With system(), both 58 STDOUT and STDERR will go the same place 57 as the script's STDOUT and STDERR, unless 56 the system() command redirects them. Backticks 55 and open() read only the STDOUT of your 54 command.

You can also use the open3() function 53 from IPC::Open3. Benjamin Goldberg provides 52 some sample code:

To capture a program's 51 STDOUT, but discard its STDERR:

use IPC::Open3;
use File::Spec;
use Symbol qw(gensym);
open(NULL, ">", File::Spec->devnull);
my $pid = open3(gensym, \*PH, ">&NULL", "cmd");
while( <PH> ) { }
waitpid($pid, 0);

To capture 50 a program's STDERR, but discard its STDOUT:

use IPC::Open3;
use File::Spec;
use Symbol qw(gensym);
open(NULL, ">", File::Spec->devnull);
my $pid = open3(gensym, ">&NULL", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0);

To 49 capture a program's STDERR, and let its 48 STDOUT go to our own STDERR:

use IPC::Open3;
use Symbol qw(gensym);
my $pid = open3(gensym, ">&STDERR", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0);

To read both 47 a command's STDOUT and its STDERR separately, you 46 can redirect them to temp files, let the 45 command run, then read the temp files:

use IPC::Open3;
use Symbol qw(gensym);
use IO::File;
local *CATCHOUT = IO::File->new_tmpfile;
local *CATCHERR = IO::File->new_tmpfile;
my $pid = open3(gensym, ">&CATCHOUT", ">&CATCHERR", "cmd");
waitpid($pid, 0);
seek $_, 0, 0 for \*CATCHOUT, \*CATCHERR;
while( <CATCHOUT> ) {}
while( <CATCHERR> ) {}

But 44 there's no real need for both to be tempfiles... the 43 following should work just as well, without 42 deadlocking:

use IPC::Open3;
use Symbol qw(gensym);
use IO::File;
local *CATCHERR = IO::File->new_tmpfile;
my $pid = open3(gensym, \*CATCHOUT, ">&CATCHERR", "cmd");
while( <CATCHOUT> ) {}
waitpid($pid, 0);
seek CATCHERR, 0, 0;
while( <CATCHERR> ) {}

And it'll be faster, too, since 41 we can begin processing the program's stdout 40 immediately, rather than waiting for the 39 program to finish.

With any of these, you 38 can change file descriptors before the call:

open(STDOUT, ">logfile");
system("ls");

or 37 you can use Bourne shell file-descriptor 36 redirection:

$output = `$cmd 2>some_file`;
open (PIPE, "cmd 2>some_file |");

You can also use file-descriptor 35 redirection to make STDERR a duplicate of 34 STDOUT:

$output = `$cmd 2>&1`;
open (PIPE, "cmd 2>&1 |");

Note that you cannot simply open 33 STDERR to be a dup of STDOUT in your Perl 32 program and avoid calling the shell to do 31 the redirection. This doesn't work:

open(STDERR, ">&STDOUT");
$alloutput = `cmd args`;  # stderr still escapes

This 30 fails because the open() makes STDERR go 29 to where STDOUT was going at the time of 28 the open(). The backticks then make STDOUT 27 go to a string, but don't change STDERR 26 (which still goes to the old STDOUT).

Note 25 that you must use Bourne shell (sh(1)) redirection 24 syntax in backticks, not csh(1)! Details 23 on why Perl's system() and backtick and 22 pipe opens all use the Bourne shell are 21 in the versus/csh.whynot article in the 20 "Far More Than You Ever Wanted To Know" collection 19 in http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz . To capture a command's STDERR and 18 STDOUT together:

$output = `cmd 2>&1`;                       # either with backticks
$pid = open(PH, "cmd 2>&1 |");              # or with an open pipe
while (<PH>) { }                            #    plus a read

To capture a command's STDOUT 17 but discard its STDERR:

$output = `cmd 2>/dev/null`;                # either with backticks
$pid = open(PH, "cmd 2>/dev/null |");       # or with an open pipe
while (<PH>) { }                            #    plus a read

To capture a command's 16 STDERR but discard its STDOUT:

$output = `cmd 2>&1 1>/dev/null`;           # either with backticks
$pid = open(PH, "cmd 2>&1 1>/dev/null |");  # or with an open pipe
while (<PH>) { }                            #    plus a read

To exchange 15 a command's STDOUT and STDERR in order to 14 capture the STDERR but leave its STDOUT 13 to come out our old STDERR:

$output = `cmd 3>&1 1>&2 2>&3 3>&-`;        # either with backticks
$pid = open(PH, "cmd 3>&1 1>&2 2>&3 3>&-|");# or with an open pipe
while (<PH>) { }                            #    plus a read

To read both 12 a command's STDOUT and its STDERR separately, it's 11 easiest to redirect them separately to files, and 10 then read from those files when the program 9 is done:

system("program args 1>program.stdout 2>program.stderr");

Ordering is important in all these 8 examples. That's because the shell processes 7 file descriptor redirections in strictly 6 left to right order.

system("prog args 1>tmpfile 2>&1");
system("prog args 2>&1 1>tmpfile");

The first command sends 5 both standard out and standard error to 4 the temporary file. The second command sends 3 only the old standard output there, and 2 the old standard error shows up on the old 1 standard out.

Score: 17

Programs themselves can't throw "exceptions", but 4 they can return nonzero error codes. You 3 can check the error code of a program run 2 with backticks or system() in Perl using $?:

$toPrint = "FAIL" if $?;

(Add 1 this line before the loop that tests @output.)

Score: 5

Check perlvar for $?. If it's set to 0, there were 6 no signals, and the return code from the 5 program is also zero. That's probably what 4 you want.

In this case, you could even just 3 use system and check its return value for being 2 zero, while redirecting the stdout and stderr 1 to /dev/null.

Score: 4

Assuming that diff errors wind up on STDERR, if 4 you'd like to be able to examine or log 3 the errors, I recommend the CPAN module 2 Capture::Tiny:

use Capture::Tiny 'capture';
my ($out, $err) = capture { system($command) };

That's like backticks, but 1 gives you STDOUT and STDERR separately.

Score: 1

There is a list of interesting ways to work 4 with the output of a backticks-enclosed 3 command at the perldoc site. You'll have to scroll down 2 to or search for "qx/STRING/" (without 1 the quotes)

Score: 0

You could also make a run through with the 2 output of 'diff -d' which will make your 1 code easier to read.

foreach (`diff -d $args`){
  if (/^Only in/){
     do_whatever();
  }
}
Score: 0

You can also:

my @output = `$command 2>\&1`;

0

More Related questions