Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Capturing stderr

by Zadeh (Beadle)
on Dec 30, 2005 at 23:10 UTC ( #520082=perlquestion: print w/ replies, xml ) Need Help??
Zadeh has asked for the wisdom of the Perl Monks concerning the following question:

I have some perl that captures the output from a command, e.g.: $output = `$cmd`; I'd like to capture the output from stderr as well though, without redirecting stderr to a file on disk--I'd like to redirect it to an array. How would I do that?

Comment on Capturing stderr
Re: Capturing stderr
by Joost (Canon) on Dec 30, 2005 at 23:17 UTC
Re: Capturing stderr
by Celada (Monk) on Dec 30, 2005 at 23:34 UTC

    Is it enough to merge the stdout together with the stderr into one stream? If so then you could do

    $output = `$cmd 2>&1`
    It sounds like you want to capture the stdout and stderr into seperate variables though. You will need this:
    use strict; use IO::Select; use POSIX; pipe(STDOUT_READ, STDOUT_WRITE) || die; pipe(STDERR_READ, STDERR_WRITE) || die; my $pid = fork(); die unless (defined($pid)); if ($pid == 0) { close STDOUT_READ; close STDERR_READ; open(STDOUT, ">&STDOUT_WRITE"); open(STDERR, ">&STDERR_WRITE"); close STDOUT_WRITE; close STDERR_WRITE; exec $cmd; POSIX::_exit(1); } my $stdout_collected = ""; my $stderr_collected = ""; close STDOUT_WRITE; close STDERR_WRITE; my $sel = IO::Select->new(); $sel->add(\*STDOUT_READ); $sel->add(\*STDERR_READ); my $remaining = 2; while ($remaining > 0) { my @ready = $sel->can_read(); for my $r (@ready) { my $got; if (sysread($r, $got, 1024) == 0) { close $r; $remaining--; } ($r == \*STDOUT_READ) ? $stdout_collected : $stderr_collected .= $got; } } undef $sel; waitpid $pid, 0; # stdout is collected in $stdout_collected # stderr is collected in $stderr_collected

    Update: Changed read to sysread

      Yeah, I'd like to capture them separately. That looks pretty involved though... seems like there should be a simpler way. I'll try that for now though, thanks.
        I once asked a similar question in Redirecting stdout/stderr to pipe. Thelonius described a method in (Re: Redirecting stdout/stderr to pipe) that captures stdout as well as stderr (seperately) to array references (see the @$outref and @$errref portions of the code).

        The code also includes detection of commands that have been running for too long or when the array's output size exceeds $g_maxlines lines.

        0xbeef

      That looks painful. Is there any reason that IPC::Open3 wouldn't work?

      -Jeff

        IPC::Open3 will indeed simplify the first half of the code, which deals with the piping and the forking. Sorry for recommending the long winded solution :-( . It won't help with reading the outputs afterwards though, you still have to use a select loop to read interleaved input from both handles. That's why ikegami's recommentation of IPC::Run looks like it's even easier (thanks!).

Re: Capturing stderr
by zentara (Archbishop) on Dec 31, 2005 at 12:26 UTC
    Here is the way to use IPC::Open3 with IO::Select to read the STDOUT and STDERR separately. It is from an similar question which I asked a few years ago.
    #!/usr/bin/perl # sgifford of perlmonks # The reason for your problem is because you're not using # IO::Select quite right. You're passing a timeout of 0 to # can_read, which asks it to return immediately if there is # no data ready yet. What I think you want to do is create # one IO::Select object with both handles, then ask it to # wait until one or both have something ready to read. # Something like this: # It's only drawback is it only outputs 1 line of bc output # so it errs on something like 234^12345 (which outputs a big number) use warnings; use strict; use IPC::Open3; use IO::Select; #interface to "bc" calculator my $pid = open3(\*WRITE, \*READ,\*ERROR,"bc"); my $sel = new IO::Select(); $sel->add(\*READ); $sel->add(\*ERROR); my($error,$answer)=('',''); while(1){ print "Enter expression for bc, i.e. 2 + 2\n"; chomp(my $query = <STDIN>); #send query to bc print WRITE "$query\n"; foreach my $h ($sel->can_read) { my $buf = ''; if ($h eq \*ERROR) { sysread(ERROR,$buf,4096); if($buf){print "ERROR-> $buf\n"} } else { sysread(READ,$buf,4096); if($buf){print "$query = $buf\n"} } } } waitpid($pid, 1); # It is important to waitpid on your child process, # otherwise zombies could be created.

    I'm not really a human, but I play one on earth. flash japh
Re: Capturing stderr
by adrianh (Chancellor) on Dec 31, 2005 at 15:12 UTC
    I have some perl that captures the output from a command, e.g.: $output = `$cmd`; I'd like to capture the output from stderr as well though, without redirecting stderr to a file on disk--I'd like to redirect it to an array. How would I do that?

    IPC::Run3 lets you do this very easily.

    use IPC::Run3; my ( @out, @err ); run3 [ $command ], undef, \@out, \@err; print map { "STDOUT = $_" } @out; print map { "STDERR = $_" } @err;

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://520082]
Approved by Joost
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (6)
As of 2014-07-10 05:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    When choosing user names for websites, I prefer to use:








    Results (199 votes), past polls