PedroA PedroA - 9 days ago 7
Perl Question

How to test the exit status from IPC::Run3

I'm trying to test the Perl module IPC::Run3 but having difficulty in checking whether a command is failed or successful.

I know that IPC::Run3 issues an exit code if something is wrong with its arguments, but what about if the arguments are ok but the command does not exist? How can I test the following example?

Having a subroutine to call Run3

sub runRun3 {

my $cmd = shift;
my ($stdout, $stderr);

run3($cmd, \undef, \$stdout, \$stderr);

# if( $? == -1 ) {
if (! $stdout and ! $stderr) {
die "Something is wrong";
} else {
print "OK \n";
}

}


when command
$cmds[0]
below is executed (the
ls
command of *nix systems) it prints
OK
as expected, but with command
$cmds[1]
it just says
No such file or directory at ./testrun3.pl line 18
.
With a test to the exit code I want it to print
Something is wrong
instead.

#!/usr/bin/perl

use warnings;
use strict;

use IPC::Run3;

my @cmds = qw(ls silly);

runRun3($cmds[0]);
runRun3($cmds[1]);


Or what would be the best alternative to IPC::Run3 in cases like this? This is just an oversimplification of the process, but eventually I would like to capture STDERR and STDOUT for more complex situations.

Thanks.

Answer

A few points to go through.

First, for the direct question, the IPC::Run3 documentation tells us that

run3 throws an exception if the wrapped system call returned -1 or anything went wrong with run3's processing of filehandles. Otherwise it returns true. It leaves $? intact for inspection of exit and wait status.

The error you ask about is of that kind and you need to eval the call to catch that exception

use warnings 'all';
use strict;
use feature 'say';

my ($stdout, $stderr);

my @cmd = ("ls", "-l");

eval { run3 \@cmd, \undef, \$stdout, \$stderr };
if    ( $@        ) { print "Error: $@";                     }
elsif ( $? & 0x7F ) { say "Killed by signal ".( $? & 0x7F ); }
elsif ( $? >> 8   ) { say "Exited with error ".( $? >> 8 );  }
else                { say "Completed successfully";          }

You can now print your own messages inside if ($@) { } block, when errors happen where the underlying system fails to execute. Such as when a non-existing program is called.

Here $@ relates to eval while $? to system. If run3 didn't have a problem and $@ is false, next we need to check the status of system itself, thus $?. From docs

Note that a true return value from "run3" doesn't mean that the command had a successful exit code. Hence you should always check $?.

For variables $@ and $? see General Variables in perlvar, and system and eval pages.

A minimal version of this is to drop eval (and $@ check) and expect the program to die if run3 had problems, what should be rare, and to check (and print) the value of $?.

A note on run3 interface. With \@cmd it expects @cmd to contain a command broken into words, with the first element being the program and the rest arguments. It's not for multiple commands. And there is a difference between writing your command in a string, supported by $cmd interface, and in an array. See system for explanation.

There is a number of other related modules. Which one would suit you best depends on your exact needs. By what you mention perhaps try first IPC::System::Simple. On the other end there is IPC::Run for example, for far more power.

Comments