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

Dear monks, this code snippet works well - standalone
#!/my/selfcompiled/bin/perl-5.18.2 use strict; use warnings; use IO::File; use IPC::Open3; my $reader = IO::File->new(); my $writer = IO::File->new(); my $error = IO::File->new(); open3($writer, $reader, $error, 'ssh', '-V'); my $ssh_version = <$error>; chomp($ssh_version); printf "[%s]\n", $ssh_version;
But when used within a more sophisticated application consisting of some modules, the same code fails with eof on $error. I have no idea why this fails and how to find more details on reasons for that. Please advise how to step forward to find root cause. TIA Stefan

Replies are listed 'Best First'.
Re: Problems with open3
by salva (Canon) on Oct 15, 2014 at 10:54 UTC
    If you just want to capture the output from some external program, you can use backquotes:
    my $ssh_version = `ssh -V`; chomp $ssh_version; printf "[%s]\n", $ssh_version;

    If you really need IPC::Open3, you should know that it is a quite complex module under the hood. In my experience, one of the best ways to troubleshoot it is to use strace or some similar tool.

    Also, there are several alternative modules on CPAN that may work better for you.

      To capture STDERR with backquotes, use:

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

      If you want both, omit redirecting STDOUT to /dev/null:

      $output = `cmd 2>&1`;

      However, it is generally safer to use open3.

Re: Problems with open3
by zentara (Cardinal) on Oct 15, 2014 at 16:48 UTC
    the same code fails with eof on $error. I have no idea why this fails and how to find more details on reasons for that. Please advise how to step forward to find root cause.

    With IPC::Open3, I can see 2 possible options to see what is actually happening. one is to set the $error filehandle to 0, and redirect it to stdout. The other option to to add both $reader and $error to an IO::Select loop and collect them separately.

    Here are 2 samples:

    #!/usr/bin/perl use warnings; use strict; use IPC::Open3; use IO::Select; my $pid = open3(\*WRITE, \*READ,\*ERROR,"bc"); #if \*ERROR is false,0, STDERR is sent to STDOUT my $sel = new IO::Select(); $sel->add(\*READ); $sel->add(\*ERROR); my($error,$answer)=('',''); while(1){ print "Enter command\n"; chomp(my $query = <STDIN>); #send query to bash 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.
    or
    #!/usr/bin/perl # by pg of perlmonks # This is for lazy people. Sometime # when you compile your c/c++ program, you may get # tons of errors, especially when you miss some # include files or just start a new program. In # this case the error messages just fly away # quickly, and you get totally lost. Well, you may # increase your buffer, you may redirect your # output but for people who are lazy like me, you # want a script. # This script starts your make process thru open3, # capturing all compiling errors, and dumping them # to both screen and a file called error_log. If # the error log is short, you can just view it on # screen, otherwise vi the error_log. use IPC::Open3; use strict; use warnings; my $pid = open3(\*WRITER, \*READER, \*ERROR,"make"); open(ERROR_LOG, ">", "error_log"); while (my $line = <ERROR>) { print $line; print ERROR_LOG $line; } close(ERROR_LOG); waitpid($pid, 0); ####################################################### #or # "man script" #or # command 2>&1 | tee ./myerror.log

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