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

Discussion in 'Perl Misc' started by PerlFAQ Server, Mar 12, 2011.

  1. 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.
    PerlFAQ Server, Mar 12, 2011
    #1
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Grant Edwards

    Re: Capture output from stderr

    Grant Edwards, Aug 6, 2003, in forum: Python
    Replies:
    2
    Views:
    419
    Grant Edwards
    Aug 6, 2003
  2. Replies:
    2
    Views:
    440
    Nobody
    Oct 20, 2010
  3. Dib Urim
    Replies:
    4
    Views:
    173
    Ben Liddicott
    Nov 23, 2003
  4. Dib Urim
    Replies:
    4
    Views:
    182
    Michele Dondi
    Dec 3, 2003
  5. Jake Cahoon
    Replies:
    2
    Views:
    96
    Paul Lalli
    Jun 10, 2004
Loading...

Share This Page