1
votes

I have a small subroutine that uses IPC::Open3 (usually I use Capture::Tiny) because I wanted to use only core Perl modules. This subroutine is part of larger installation script. It captures command output, error and exit code. It works on Centos 6 (and 5) with Perl 5.10.1 (IPC::Open3 1.04) but it fails on Ubuntu 14 LTS with Perl 5.18.1 (IPC::Open3 1.13). Could someone explain to me why it fails on newer Perl and how to fix it.

sub _capture_output {
    croak( '_capture_output() needs a $cmd and options' ) unless (@_ ==  2);
    my ($cmd, $param_href) = @_;

    my $verbose = defined $param_href->{verbose}  ? $param_href->{verbose}  : 0;   #default is silent
    print "Report: COMMAND is: $cmd\n" if $verbose;

    local $| = 1;   #autoflush
    my ( $in, $out, $err );
    open my ($in_fh),  '<', \$in;
    open my ($out_fh), '>>', \$out;
    open my ($err_fh), '>>', \$err;

    my $pid = open3($in_fh, $out_fh, $err_fh, $cmd);

    my $stdout = $out;
    my $stderr = $err;
    $stdout = '' if !defined $stdout;
    $stderr = '' if !defined $stderr;

    waitpid( $pid, 0 ) or die "$!\n";
    my $exit =  $? >> 8;

    if ($verbose == 2) {
        print 'STDOUT is: ', "$stdout", "\n", 'STDERR  is: ', "$stderr", "\n", 'EXIT   is: ', "$exit\n";
    }

    return  $stdout, $stderr, $exit;
}

It fails on line with open3 call with error:

Report: COMMAND is: plenv --version
Uncaught exception from user code:
open3: exec of plenv --version failed at ./Perlinstall.pm line 175.
IPC::Open3::_open3('open3', 'GLOB(0x27819a0)', 'GLOB(0x2781730)', 'GLOB(0x2781b68)', 'plenv --version') called at /usr/share/perl/5.18/IPC/Open3.pm line 250

When I try this command on command line I get same error code but different error. Centos6:

$ plenv --version
-bash: plenv: command not found
$ echo $?
127

Ubuntu14:

$ plenv --version
No command 'plenv' found, did you mean:
Command 'p7env' from package 'libnss3-tools' (main)
plenv: command not found
$ echo $?
127
1
What do you expect it to do when plenv is not installed?cjm
What do you mean it works on Centos 6? You said that what you run your command from the command line on Centos 6, you get an error.mob
You can't use different handles for STDOUT and STDERR without using select (or IO::Select). If you do, you can have deadlocks.ikegami
I use error to check for existence of program. If I catch exit code other than zero, I install this program and try next in line.mocnii

1 Answers

4
votes

It's not clear what you think the problem is, but open3 is behaving as documented.

It doesn't return on failure: it just raises an exception matching /^open3:/.

The program you are trying to execute doesn't exist, so open3 throws an exception. I think you are wondering why $? isn't set and why nothing was printed to the the handle in $stderr, but that shouldn't be a surprise since the program never ran.

Keep in mind that exec (used by open3) will bypass the shell if the command is a string with no shell characters except whitespace, so you'll get different results from executing

plenv --version         
# Same as: open3(..., ..., ..., 'plenv', '--version')
# Exception: Can't find plenv

and

plenv '--version'
# Same as: open3(..., ..., ..., '/bin/sh', '-c', q{plenv '--version'})
# Shell exits with an error in ($? >> 8)

You can catch exceptions using eval BLOCK.