30

I'm new to Perl and want to know of a way to run an external command (call it prg) in the following scenarios:

  1. Run prg, get its stdout only.
  2. Run prg, get its stderr only.
  3. Run prg, get its stdout and stderr, separately.
brian d foy
  • 129,424
  • 31
  • 207
  • 592
gameover
  • 11,813
  • 16
  • 59
  • 70
  • 9
    Do yourself a big favor and spend some time on perldoc.perl.org -- for starters the "Tutorials" and "FAQs" areas. Also run this to familiarize yourself with Perl's system for accessing help info on the command-line: `perldoc --help`. The answer to your specific question could have been found directly by command-line searches such as these: `perldoc -q capture` or `perldoc -q external`. – FMc Mar 17 '10 at 12:19
  • You may want to look into `qx`, it might have ways to separate the streams and is easy to run e.g. `qx("command");` – James Oravec Sep 25 '14 at 19:39

4 Answers4

42

You can use the backtics to execute your external program and capture its stdout and stderr.

By default the backticks discard the stderr and return only the stdout of the external program.So

$output = `cmd`;

Will capture the stdout of the program cmd and discard stderr.

To capture only stderr you can use the shell's file descriptors as:

$output = `cmd 2>&1 1>/dev/null`;

To capture both stdout and stderr you can do:

$output = `cmd 2>&1`;

Using the above you'll not be able to differenciate stderr from stdout. To separte stdout from stderr can redirect both to a separate file and read the files:

`cmd 1>stdout.txt 2>stderr.txt`;
codaddict
  • 445,704
  • 82
  • 492
  • 529
  • 2
    Another way to read stdout and stderr separately, without using temporary files, is to use IPC::Open3. – dave4420 Mar 17 '10 at 10:55
  • 3
    Every time backticks are used, a kitten dies. In shell, use $(). In perl, use qx. – William Pursell Mar 17 '10 at 15:40
  • Thanks for that, I killed 20 kittens before I used qx ;) – Severun Feb 21 '14 at 02:12
  • The second solution `cmd 2>&1 1>/dev/null` kills both stdout and stderr. Somehow the shell doesn't identify that the two redirections are separate!! – L__ Apr 11 '16 at 16:27
  • How do you pass arguments to `cmd` with backticks, ensuring proper escaping? – x-yuri Nov 02 '18 at 10:30
  • Fails: `perl -e 'my $f = ".../a b"; print(qx/file $f/);'`. Succeeds: `perl -e 'use IPC::Open2; my $f = ".../a b"; open2(my $out, my $in, "file", $f); print(readline($out))'` – x-yuri Nov 02 '18 at 10:59
  • It is worth adding that the command return code [is stored at `$?`](https://metacpan.org/pod/perlvar#$?), in case you want to peek at it before making use of the command output. – Hi-Angel Apr 27 '20 at 13:04
  • @L__ In Bash 4.3.48 `2>&1 1>/dev/null` works correctly (stderr goes to terminal, stdout is discarded). – U. Windl Aug 23 '23 at 07:51
  • `\`cmd 1>stdout.txt 2>stderr.txt\`;` is a non-Perl solution, and using fixed filenames is probably a bad idea security-wise. – U. Windl Aug 23 '23 at 07:53
14

In most cases you can use the qx// operator (or backticks). It interpolates strings and executes them with the shell, so you can use redirections.

  • To capture a command's STDOUT (STDERR is unaffected):

    $output = `cmd`;
    
  • To capture a command's STDERR and STDOUT together:

    $output = `cmd 2>&1`;
    
  • To capture a command's STDERR but discard its STDOUT (ordering is important here):

    $output = `cmd 2>&1 1>/dev/null`;
    
  • To exchange a command's STDOUT and STDERR in order to capture the STDERR but leave its STDOUT to come out the old STDERR:

    $output = `cmd 3>&1 1>&2 2>&3 3>&-`;
    
  • 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");
    
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
11

You can use IPC::Open3 or IPC::Run. Also, read How can I capture STDERR from an external command from perlfaq8.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
  • 1
    It'd be great to have an example here. You can use my [comment](https://stackoverflow.com/questions/2461472/how-can-i-run-an-external-command-and-capture-its-output-in-perl#comment93128903_2461530) for inspiration. – x-yuri Nov 02 '18 at 11:00
2

Beware about the answer of Eugene (can't comment on his answer), just above, that the syntax to exchange SDTOUT and STDERR is valid on Unixes (Unixen-like shells such as ksh, or bash I guess) but not under Windows CMD (error: 3>& was unexpected at this time.).

The appropriate syntax under Windows CMD and Perl on Windows is:

perl -e "$r=qx{nslookup 255.255.255.255 2>&1 1>&3 3>&2};

Note that the command:

nslookup 255.255.255.255

will produce (something like) on STDOUT:

Server:  mymodem.lan
Address:  fd37:c01e:a880::1

and on STDERR:

*** mymodem.lan can't find 255.255.255.255: Non-existent domain

You can test that this syntax works with the following CMD/Perl syntax:

First:

perl -e "$r=qx{nslookup 255.255.255.255 2>&1 1>&3 3>&2}; $r=~s/[\n\r]//eg; print qq{on STDOUT qx result=[$r]};"

you get: Server: mymodem.lan Address: fd37:c01e:a880::1 on STDOUT qx result=[*** mymodem.lan can't find 255.255.255.255: Non-existent domain]

Then

perl -e "$r=qx{nslookup 255.255.255.255 2>&1 1>&3 3>&2}; $r=~s/[\n\r]//eg; print STDOUT qq{on STDOUT qx result=[$r]};" 2>&1 1>NUL:

you get: Server: mymodem.lan Address: fd37:c01e:a880::1

QED [fr:CQFD]

Note that it is not possible to get BOTH stderr and stdout as returned string for a qx or backticks command. If you know for sure that the err text returned by your spawned command is of length N lines, you can still redirect STDERR to STDOUT like describe by Eugene and others but capture your qx returned text in an array instead of as scalar string. The STDERR flow will be returned into the array before the STDOUT so that the N first lines of your array are the SDTERR lines. Like:

@r=qx{nslookup 255.255.255.255 2>&1};
$r[0] is "*** mymodem.lan can't find 255.255.255.255: Non-existent domain"

But of course you must be sure that there is an err text on STDERR and of strictly N lines (stored in @r[0..N-1]). If not, the only solution is using temp files as described above.

  • 1
    It may be better to close stream 3 afterwards with `3>nul` (haven't tested, https://stackoverflow.com/questions/12273866/is-there-a-way-to-redirect-only-stderr-to-stdout-not-combine-the-two-so-it-can https://stackoverflow.com/questions/13299317/io-redirection-swapping-stdout-and-stderr ) – user202729 May 13 '23 at 03:51
  • Tested redirection 1,2,3 under Windows (7/64SP1 and 10/64) with Perl qx{}: no side effect noticed after multiple qx in the same Perl script, while NOT "closing" stream 3. Strawberry Perl. I guess, either CMD or Perl restores descriptors when it terminates the fork/spawn. The example provided in the stackoverflow link deals with Unix shell and remains in the same process. I guess that should be the origin of different behaviours. – Gilles Maisonneuve May 22 '23 at 08:44