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 versions of these, unless the command redirects them. Backticks and open read only the STDOUT of your command.

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! Details on why Perl's system and backtick and pipe opens all use the Bourne shell are in CPAN/doc/FMTEYEWTK/versus/csh.whynot .

You may also use the IPC::Open3 module (part of the standard perl distribution), but be warned that it has a different order of arguments from IPC::Open2 (see Open3).