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

Hi: I have a script that I am executing using FileHandles in perl. My script generates STDERR, STDOUT(obviously!!!) and needs to take care of STDIN (user input from keyboard, ie it is interactive). I need STDERR (for post processing!).

I am using something like:

open(FH, "$Exec_Cmd 2>&1 |"); while (<FH>) { print $_; # I have STDERR captured in $_ but then this # does not handle STDIN }
Meanwhile, If I try:
open(FH, "$Exec_Cmd 0>&2 |"); while (<FH>) { print $_; # I take care of STDIN but then this # does not handle STDERR }
Can anyone please tell me how can I capture STDERR as well handle STDIN simultaneously.

Thanks and Regards, Tony

Replies are listed 'Best First'.
Re: Handling STDERR and STDIN in Perl
by jeffa (Bishop) on May 09, 2004 at 15:14 UTC

    This is not easy stuff. Recipe 16.10 from the Perl Cookbook recommends using IPC::Open3, which brings fork into the picture. Here is Example 16.2 reprinted (i threw in a few minor adjustments). You should be able to wrap this in a sub and pass in the shell command, but please do RTFM the module docs before you just add this to production code, if any.

    # cmd3sel - control all three of kids in, out, and error. use IPC::Open3; use IO::Select; my $cmd = "grep vt33 /none/such - /etc/termcap"; my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $cmd); $SIG{CHLD} = sub { warn "REAPER: status $? on $pid\n" if waitpid($pid, 0) > 0 }; print CMD_IN "This line has a vt33 lurking in it\n"; close(CMD_IN); my $selector = IO::Select->new(); $selector->add(*CMD_ERR, *CMD_OUT); while (my @ready = $selector->can_read) { foreach my $fh (@ready) { if (fileno($fh) == fileno(CMD_ERR)) {print "STDERR: ", scalar <CMD_ERR>} else {print "STDOUT: ", scalar <CMD_OUT>} $selector->remove($fh) if eof($fh); } } close(CMD_OUT); close(CMD_ERR);
    Hope this helps, and don't ++ me ... just buy the Cookbook. :)

    UPDATE: have you looked into Expect?

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      Thanks a lot Jeffa: But the program still does not wait for STDIN. Consider $cmd = /home/script.pl where: script.pl contains: #!/usr/local/bin/perl $a = <STDIN>; print $a; Here the program doesn't wait for STDIN. This is my requirement. Thanks Tony
Re: Handling STDERR and STDIN in Perl
by dave_the_m (Monsignor) on May 09, 2004 at 15:13 UTC
    I think you want IPC::Open3. From the synopsis:
    my($wtr, $rdr, $err); $pid = open3($wtr, $rdr, $err, 'some cmd and args', 'optarg', ...);
    update: whoops, I typed IO::Open3 - I meant IPC::Open3!
Re: Handling STDERR and STDIN in Perl
by zentara (Cardinal) on May 10, 2004 at 12:58 UTC
    But the program still does not wait for STDIN. Consider $cmd = /home/script.pl where: script.pl contains: #!/usr/local/bin/perl $a = <STDIN>; print $a; Here the program doesn't wait for STDIN. This is my requirement.

    I'm not sure about your exact situation, but I've used the following trick to get STDIN from a program that I have opened with IPC::Open3. You need to capture the output from IPC::Open3, and test it with a regex for your prompt. Then:

    { local *STDIN; open( STDIN, "< /dev/tty" ); my $ok = <STDIN>; }
    Then write it to IPC:Open3.

    I'm not really a human, but I play one on earth. flash japh