Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Perl > Perl Misc > FAQ 8.25 How can I capture STDERR from an external command?

Reply
Thread Tools

FAQ 8.25 How can I capture STDERR from an external command?

 
 
PerlFAQ Server
Guest
Posts: n/a
 
      03-12-2011
This is an excerpt from the latest version perlfaq8.pod, which
comes with the standard Perl distribution. These postings aim to
reduce the number of repeated questions as well as allow the community
to review and update the answers. The latest version of the complete
perlfaq is at http://faq.perl.org .

--------------------------------------------------------------------

8.25: How can I capture STDERR from an external command?

There are three basic ways of running external commands:

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

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

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

To capture a program's 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 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 capture a program's STDERR, and let its 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 a command's STDOUT and its STDERR separately, you can
redirect them to temp files, let the 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 there's no real need for both to be tempfiles... the following
should work just as well, without 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 we can begin processing the program's
stdout immediately, rather than waiting for the program to finish.

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

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

or you can use Bourne shell file-descriptor redirection:

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

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

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

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

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

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

Note that you *must* use Bourne shell (sh(1)) redirection syntax in
backticks, not csh(1)! Details on why Perl's "system()" and backtick and
pipe opens all use the Bourne shell are in the versus/csh.whynot article
in the "Far More Than You Ever Wanted To Know" collection in
http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz . To capture a command's
STDERR and 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 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 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 a command's STDOUT and STDERR in order to capture the STDERR
but leave its STDOUT 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 a command's STDOUT and its STDERR separately, it's easiest
to redirect them separately to files, and then read from those files
when the program is done:

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

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

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

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



--------------------------------------------------------------------

The perlfaq-workers, a group of volunteers, maintain the perlfaq. They
are not necessarily experts in every domain where Perl might show up,
so please include as much information as possible and relevant in any
corrections. The perlfaq-workers also don't have access to every
operating system or platform, so please include relevant details for
corrections to examples that do not work on particular platforms.
Working code is greatly appreciated.

If you'd like to help maintain the perlfaq, see the details in
perlfaq.pod.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: How include a large array? Edward A. Falk C Programming 1 04-04-2013 08:07 PM
Script to capture stderr of subprocess jslowery@gmail.com Python 2 10-20-2010 07:36 PM
How can I capture STDERR and get an exit code? Jake Cahoon Perl Misc 2 06-10-2004 08:04 PM
capture stderr Simon Strandgaard Ruby 5 02-10-2004 03:47 PM
Re: Capture output from stderr Grant Edwards Python 2 08-06-2003 03:47 AM



Advertisments