10

Let's say I want to run an external program from my script with backticks and at the same time I want to capture both STDOUT and STDERR but in two different variables. How can I do that? For istance if I run this script...

my $cmd = `snmpwalk -v $version -c $community $hostname $oid`;

...if there is no error everything works just fine BUT if the command raise an error this error will be printed on the command line and I don't want that to happen. I want to capture the error as well. Nothing has to be printed on the screen. Any ideas?

raz3r
  • 3,071
  • 8
  • 44
  • 66
  • 2
    You might want to take a look at [Net-SNMP](http://search.cpan.org/~dtown/Net-SNMP-v6.0.1/). With that you'll have full error control and needn't worry about redirecting output someplace else. – Linus Kleen Dec 05 '11 at 11:40

4 Answers4

14

You needn't go all the way to open3, which IIRC is only for when you need to read and write to an external command, and even then there are other methods.

For your problem I suggest using Capture::Tiny, which can capture (or even tee) the STDOUT and STDERR from anything run inside its block. For example, per your question:

#!/usr/bin/env perl

use strict;
use warnings;

use Capture::Tiny qw/capture/;

...

my ($stdout, $stderr) = capture {
  system ( "snmpwalk -v $version -c $community $hostname $oid" );
};

For another example consider this functioning code:

#!/usr/bin/env perl

use strict;
use warnings;

use Capture::Tiny qw/capture/;

my ($stdout, $stderr) = capture {
  system ( "echo 'hello'" );
  system ( "date" );
  warn "Arg1!";
};

print "STDOUT:\n$stdout";
print "STDERR:\n$stderr";

which just gave me:

STDOUT:
hello
Mon Dec 19 23:59:06 CST 2011
STDERR:
Arg1! at ./test.pl line 11.
Joel Berger
  • 20,180
  • 5
  • 49
  • 104
8

The only way to do this with backticks is to redirect to a file inside the shell command:

   my $cmd = `snmpwalk -v $version -c $community $hostname $oid 2>error.dat`;

If you want to capture the STDERR inside your script, you need IPC::Open3 instead of backticks

Colin Fine
  • 3,334
  • 1
  • 20
  • 32
  • Well I don't have to handle errors (because they're more like warnings to me) so that solution may be suitable for me. I'm gonna try that right now :) – raz3r Dec 05 '11 at 11:37
  • It doesnt work :( error.dat is empty and errors are still raised on the command line :( – raz3r Dec 05 '11 at 11:39
  • 1
    I don't know why it shouldn't work. If it's creating the file, then the shell is working as expected. I can only suppose that snmpwalk is outputting the error by some other mechanism than printing on STDERR, but I don't know what. I was going to say, if you don't want the output anyway, '2>/dev/null', but if the error.dat case doesn't work, nor will that, probably. – Colin Fine Dec 05 '11 at 11:47
  • That's really strange, in fact I never had this problem before. I looked at snmpget help and there are some additional log options that might help but they don't seem to work. – raz3r Dec 05 '11 at 14:04
  • 1
    it is not "the only way" you can redirect strerr to stdout. `2>&1` – Gerhard Mar 24 '21 at 08:45
5

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

Robino
  • 4,530
  • 3
  • 37
  • 40
Pablo Marin-Garcia
  • 4,151
  • 2
  • 32
  • 50
  • *Backticks and open() read only the STDOUT of your command.* So it looks like I can't capture STDERR, I must use Open3, thanks for the answer btw! – raz3r Dec 05 '11 at 16:07
  • 1
    while this faq is correct, it is unnecessarily complicated and IMO incomplete. While the basic Perl language makes this difficult, there are modules which can help, see my answer for examples using [`Capture::Tiny`](http://p3rl.org/Capture::Tiny) – Joel Berger Dec 20 '11 at 06:06
0

IO::CaptureOutput

is a very convenient wrapper for what you want to do.

John
  • 573
  • 2
  • 6