Perl Script to Capture Stderr and Stdout of Command Executed in Back-Quotes

Perl script to capture stderr and stdout of command executed in back-quotes

my $output = `ssh login.com git clone --bare user@login.com:/nfs/repo/ /nfs/repo//4124/ 2>&1`;

The 2>&1 at the end sends standard error to the same place as standard output, which is captured by the back-quotes.

How to capture both STDOUT and STDERR in two different variables using Backticks in Perl

In the Perl FAQ you have different options depending how do you want to proceed:

http://perldoc.perl.org/perlfaq8.html#How-can-I-capture-STDERR-from-an-external-command%3f

Perl - Using backquotes missing output

Here's how to capture the STDOUT & STDERR of a spawned process using backticks:

my $output = join('', `command arg1 arg2 arg3 2>&1`);

How it works has no dependence whatsoever on newlines in the output of command.

If you also need to send text to command's STDIN, then use IPC::Open3.


Cleaned your code up a bit. It works for me.

use strict;
use warnings;
use 5.10.0;

# something missing here to set up HTML file handle
# something missing here to set up $admincmd

print HTML q{<h2 id='pol'>Policy Configuration\n</h2>};
my @bpllist = `"$admincmd/bppllist.exe"`
or die "$admincmd/bppllist.exe not found or could not be executed\n";
for my $policy (@bpllist) {
print HTML q{<div><table class='table'>\n};
my @lines = `$admincmd/bpplinfo.exe $policy -U 2>&1`;
print HTML qq{\t<tr>\n\t<td><b>Policy name: <b></td><td>$policy</td>\n\t</tr>\n} ;
for my $pair (@lines) {
chomp($pair); # only remove newlines, not other characters
my ($var, $value) = split /:/, $pair, 2;
$var //= '';
$value //= '';
print HTML qq{\t<tr>\n\t<td>$var</td><td>$value</td>\n\t</tr>\n} ;
}
print HTML q{</table></div>};
}

Update 2

You appear to be doing this on windows?

I don't think the 2>&1 trick will work there.

How can I read the error output of external commands in Perl?

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

$toPrint = "FAIL" if $?;

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

perl get ouput in a file along with STDERR and STDOUT

Cat the file once its done.

my ($output) = `ssh login.com ls /tmp/ > filename 2>&1;cat filename`
print "\n=========$output============";

Edit: If keeping the exit code is important (and I think the answers in https://stackoverflow.com/a/9784064/1137055 are more complete and less hacky):

`ssh login.com ls /tmp/ > filename 2>&1`; # run command and output to a file
my $code = $?;
my $output = { local $/ = undef; open FILE, "file" or die "Couldn't open file: $!"; $string = <FILE>; close FILE; } # "slurp" the file in using perl
print "\n=========$output============$code========\n";

Or an even less ideal solution:

`ssh login.com ls /tmp/ > filename 2>&1`; # run command and output to a file
my $code = $?;
my $output = `cat filename` # read the file by shell
print "\n=========$output============$code========\n";

How to discard STDERR from an external command in Perl?

See http://perldoc.perl.org/perlfaq8.html#How-can-I-capture-STDERR-from-an-external-command%3f

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.

How can I store the result of a system command in a Perl variable?

From Perlfaq8:

You're confusing the purpose of system() and backticks (``). system() runs a command and returns exit status information (as a 16 bit value: the low 7 bits are the signal the process died from, if any, and the high 8 bits are the actual exit value). Backticks (``) run a command and return what it sent to STDOUT.

$exit_status   = system("mail-users");
$output_string = `ls`;

There are many ways to execute external commands from Perl. The most commons with their meanings are:

  • system() : you want to execute a command and don't want to capture its output
  • exec: you don't want to return to the
    calling perl script
  • backticks : you want to capture the
    output of the command
  • open: you want to pipe the command (as
    input or output) to your script

Also see How can I capture STDERR from an external command?

Perl script redirect stderr and stdout to a file

script.pl ... >myscript.log 2>&1
  • >myscript.log opens the file for writing and uses that handle for STDOUT.
  • 2>&1 makes STDERR the same handle as STDOUT.

Order matters.


For csh, the simplest way is:

sh -c 'script.pl ... >myscript.log 2>&1'

Ok, I'm just kidding :) You can actually do

script.pl ... >& file

csh's redirection support is far more limited than that of sh and its derivatives, but this is something it can do.



Related Topics



Leave a reply



Submit