Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister

Capturing STDERR using IO::Handle

by rvosa (Curate)
on Nov 26, 2007 at 08:57 UTC ( #652944=perlquestion: print w/replies, xml ) Need Help??
rvosa has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks,

I am trying to capture the STDERR from a process I'm launching with system(). I thought I had it all figured out by doing:
use IO::Handle; my @logmessages; my $stderr = IO::Handle->new; $stderr->autoflush( 1 ); $stderr->fdopen( fileno( STDERR ), 'r' ); system( 'command', 'with', 'args' ); while( defined( my $line = $stderr->getline ) ) { push @logmessages, $line; } $stderr->close; # now do something useful with @logmessages
However, things seem to get stuck in the while loop. What am I doing wrong? What's the canonical way to capture STDERR (I know, I know, TMTOWTDI so I may not need the canonical way, just one that works).


Replies are listed 'Best First'.
Re: Capturing STDERR using IO::Handle
by ikegami (Pope) on Nov 26, 2007 at 09:28 UTC

    IPC::Open3's open3 is one way.

    use IPC::Open3 qw( open3 ); use Symbol qw( gensym ); my $pid = open3( my $to_chld = gensym(), my $fr_chld = gensym(), my $fr_chld_err = gensym(), 'command', 'with', 'args' ); close($to_chld); close($fr_chld); my @logmessages = <$fr_chld_err>; waitpid($pid, 0);


      IO::CaptureOutput is another way.

      use IO::CaptureOutput qw/capture_exec/; my ($stdout, $stderr) = capture_exec( 'command', 'with', 'args' );


      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: Capturing STDERR using IO::Handle
by ysth (Canon) on Nov 26, 2007 at 09:11 UTC
    You are suffering from a little confusion. The fdopen method doesn't associate some other filehandle with your IO::Handle; it's the other way around: after the fdopen, your $stderr will be associated with whatever STDERR was before (only with the incorrect 'r' mode).

    To capture STDERR of a system call, you'd want the stderr output to go to some temporary file and then (rewind if necessary and) read from the file.

Re: Capturing STDERR using IO::Handle
by phio (Acolyte) on Nov 26, 2007 at 11:44 UTC
    I think you can get what you need here. Also, you can find some other examples in the 'qx' section in perlop
Re: Capturing STDERR using IO::Handle
by educated_foo (Vicar) on Nov 26, 2007 at 14:04 UTC
    Perlfunc has an example that does exactly what you want under open.

      I don't see anything of the kind. Could you be more specific, please?

      Remember tie(*HANDLE, ...) and open(..., \$var) don't produce system file handles, so they can't be inherited by child processes.

        Here is a script that saves, redirects, and restores STDOUT and STDERR using various methods:
        #!/usr/bin/perl open my $oldout, ">&STDOUT" or die "Can't dup STDOUT: $!"; open OLDERR, ">&", \*STDERR or die "Can't dup STDERR: $!"; open STDOUT, '>', "foo.out" or die "Can't redirect STDOUT: $!" +; open STDERR, ">&STDOUT" or die "Can't dup STDOUT: $!"; select STDERR; $| = 1; # make unbuffered select STDOUT; $| = 1; # make unbuffered print STDOUT "stdout 1\n"; # this works for print STDERR "stderr 1\n"; # subprocesses too open STDOUT, ">&", $oldout or die "Can't dup \$oldout: $!"; open STDERR, ">&OLDERR" or die "Can't dup OLDERR: $!"; print STDOUT "stdout 2\n"; print STDERR "stderr 2\n";
        It would be straightforward to then slurp in the contents of the saved-off files. Or did I misunderstand the question?
Re: Capturing STDERR using IO::Handle
by DrHyde (Prior) on Nov 26, 2007 at 11:44 UTC
      from Tie::STDERR docs:

      The module will catch all output, including your explicit prints to STDERR... However, if you run external command (system, ``), stderr output from that process won't be caught.

      So, it is not what the OP is looking for!

Re: Capturing STDERR using IO::Handle
by Sidhekin (Priest) on Nov 26, 2007 at 15:48 UTC

    This can't be done with simple ties.

    It can be done with Test::Trap, though. :) (Shameless as ever ....)

    use Test::Trap qw/ :flow:stderr(systemsafe) /; # or similar trap { system 'command', 'with', 'args' }; my @logmessages = split /\n/, $trap->stderr; # chomped! or season to t +aste

    Not exclusively for use in test scripts, indeed. :)

    print "Just another Perl ${\(trickster and hacker)},"
    The Sidhekin proves Sidhe did it!

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://652944]
Approved by Corion
[erix]: yeah, I ask for the text (I don't see signatures)
[Discipulus]: dear brother karl has "Furthermore I consider that Donald Trump must be impeached as soon as possible" in his sig
[erix]: ah, thanks Discipulus
[1nickt]: erix perhaps you could kindly point out your technique to roho and everyone will be happy.
[Discipulus]: but i'm also animalist, so i' hirted by 1nickt's sig: The way forward always starts with a mammal test... grin..
[erix]: I might if I did remember. It's probably somewhere in the site docs
[uhClem]: I don't favor that sort of thing around here but the place for our politics is wherever we live.
[uhClem]: And besides, the objection to it was off-topic.
[Discipulus]: 1nickt hide signature is a positive action; i think is not a solution here

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (12)
As of 2017-06-22 12:33 GMT
Find Nodes?
    Voting Booth?
    How many monitors do you use while coding?

    Results (519 votes). Check out past polls.