nomis80 has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

I want to open a process and read only its STDERR. This is very important because of STDOUT and STDERR interlacing. I can't just process one and then process the other because then STDOUT and STDERR will be deinterlaced. Anyway, here's what I tried using IPC::Open3:

my ($wtr, $rdr, $err); $wtr = '<&'; $rdr = '>&'; my $pid = open3($wtr, $rdr, $err, $program, @arguments); while (<$err>) { print STDERR $_; process_stderr($_); }

According to the IPC::Open3 manpage, the special strings '<&' and '>&' should do what I want, I think. I think I'm not understanding the manpage correctly.

I've tried something ugly using fork(), I've tried a plain system(), I've tried coding it in C, etc. I searched the archives before posting, but could find nothing. Please help me monks!

Thanks!

Replies are listed 'Best First'.
Re: Reading only STDERR
by Roy Johnson (Monsignor) on Apr 06, 2005 at 13:47 UTC
    parent.pl:
    #!perl -l use IPC::Open3; $pid = open3(\*WTRFH, \*RDRFH, \*ERRFH, 'perl child.pl'); while (<ERRFH>) { print "Received error message <$_>"; }
    child.pl:
    #!perl for (1..5) { print STDERR "Message $_\n"; }
    output of perl parent.pl:
    Received error message <Message 1 > Received error message <Message 2 > Received error message <Message 3 > Received error message <Message 4 > Received error message <Message 5 >

    Caution: Contents may have been coded under pressure.
      Sure, but then the STDOUT of child.pl gets lost. Try adding a line in child.pl:
      #!perl for (1..5) { print STDERR "Message $_\n"; print STDOUT "STDOUT $_\n"; }
      You'll see that what's printed to STDOUT doesn't get displayed, because it ends up in parent.pl's RDRFH. I want the STDOUT of child.pl to correspond to parent.pl's STDOUT, not to be a pipe.
        I hadn't discerned from your original post what you wanted done with STDOUT. To have it sent to parent's STDOUT, you do this in parent.pl:
        $pid = open3(\*WTRFH, '>&STDOUT', \*ERRFH, 'perl child.pl');

        Caution: Contents may have been coded under pressure.
        I want to open a process and read only its STDERR

        So, do you want STDOUT or not? Your first questions states very clearly that you want to read only STDERR. It's very hard to read someones mind to know they do not ask want they want to know.... :-(
        Pleas learn to ask questions in a proper way

        Paul

Re: Reading only STDERR
by DrWhy (Chaplain) on Apr 06, 2005 at 14:00 UTC
    You might try investigating IPC::Run instead of open3. It is better for some purposes.

    --DrWhy

    "If God had meant for us to think for ourselves he would have given us brains. Oh, wait..."

Re: Reading only STDERR
by eieio (Pilgrim) on Apr 06, 2005 at 13:44 UTC
    I've used a trick like the following in a shell to strip stdout from stderr:
    ( command > log.out ) >& log.err
    I believe that the parens result in a new sub-shell being created such that stdout is redirected by the time stderr is redirecdted to log.err.
      Thanks for your reply, but that's specifically what I don't want. If I first process STDERR and then print out what is in log.out, STDERR and STDOUT will be deinterlaced. That's not what I want. They need to stay (reasonably) interlaced.
Re: Reading only STDERR
by zentara (Cardinal) on Apr 07, 2005 at 11:56 UTC
    In addition to Roy Johnson's answer, you can Use IO::Select with IPC::Open3, to read both STDOUT and STDERR simultaneously, but separately.
    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);

    I'm not really a human, but I play one on earth. flash japh
Re: Reading only STDERR
by tweetiepooh (Hermit) on Apr 06, 2005 at 14:36 UTC
    how about
    parent.pl
    #!perl # call child with normal output going to STDERR print `child.pl 1>&2`;

    child.pl
    #!perl for (1..5) { print STDERR "Error $_\n"; print STDOUT "Message $_\n"; }
Re: Reading only STDERR
by nobull (Friar) on Apr 06, 2005 at 18:43 UTC
    Not strictly a Perl solution, but using the rediction features of Unix shell:
    my $STDERR_of_program = `program 2>&1 >/dev/null`;