Reputation: 8897
I'm using Perl 5.10.1 on Ubuntu 11.04. I want Perl to execute a shell script and exit with the same code the shell script exits. But this isn't working for me ...
system($runCmd) or die("Failed to run \"$runCmd\": $!");
I have confirmed that running the "$runCmd" by itself returns an exit code of 255, but the "die" clause isn't getting invoked. How do I exit with the correct code or at least fail for non-success codes?
Another minor requirement is that I want the output from $runCmd to be printed to the screen.
Upvotes: 22
Views: 81020
Reputation: 3284
I suggest checking the $?
variable (a.k.a. $CHILD_ERROR
is you use English;
pun intended). This allows for a more thorough inspection of how your system()
call turns out.
The thing to understand is that $?
is a 16-bit word (art credit to @ikegami):
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 15| 14| 13| 12| 11| 10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
\-----------------------------/ \-/ \-------------------------/
Exit code core Signal that killed
(0..255) dumped (0..127)
(0..1)
system($runCmd);
if ($? == -1) { # Failed to start program / error of the wait(2) system call
die "Failed to execute '$runCmd': $!";
} elsif ($? & 127) { # Check for non-zero signal
die "'$runCmd' died with signal", ($? & 127), ($? & 128) ? 'with' : 'without', " coredump";
} else { # No kill signal, check exit code.
my $exit_code = $? >> 8; # This is the number you said to be 255.
# print "'$runCmd' exited with value ", $exit_code, "\n";
if ($exit_code == 255) {
die("Failed to run \"$runCmd\": $!");
} else {
# can do additional checks for other exit codes if desired
}
exit $exit_code;
}
There is a noteworthy "alternative" if you really don't want to mess with $?
. Basically, just use IPC::System::Simple. Note that it isn't a core module, so you need to install it.
Using it is simple:
use IPC::System::Simple qw(system); # system() will now die properly
my $exit_value = system($runCmd);
my $exit_value2 = system($runCmd,@args); # Alternative form that avoids shell if @args
If you don't want to override system
you could use run
instead:
use IPC::System::Simple qw(run);
my $exit_value = run($runCmd);
my $exit_value2 = run($runCmd,@args);
Upvotes: 2
Reputation: 34612
If system
returns 255
, you need an and
conditional.
system
returns zero on successful execution. Also, die
will modify your script's exit code. Instead warn
and return the last exit code like this:
system($cmd) and do {
warn "Failed to run $cmd. Exit code is $?";
exit $? >> 8;
};
In order to catch the program's output, use the backtick (`) operator:
my $output = `$cmd`;
if ($?) {
warn ...;
exit $? >> 8;
}
The backtick operator only captures STDOUT
, so for all error messages (which usually go to STDERR
) to be captured, modify $cmd
and append 2>&1
to it.
Notice the right shift by eight bits on the $?
global.
Credits go to @musiKk: The perl documentation on system()
states how to properly retrieve the actual exit status.
Upvotes: 13
Reputation: 140
If system()'s seeming backwardness bothers you, you can always make it more palatable by doing something like:
my $err = system '/something/that/may/not/work';
if ($err) {
warn "@{[$! || 'undefined error']}\n";
exit $err >> 8;
}
Your $runCmd, BTW, if it prints to the screen (and this is run from the command line and you haven't redirected the output, etc.) will print to the screen. It's just that IT will print it to the screen. Perl won't be responsible for that or know (or care) what it's printing. If that's all you want, and you don't want to systematically analyse or manipulate the output of $runCmd, you're golden. Try it:
perl -e "system 'ls -Fahl'"
It also won't interfere with your $runCmd's STDOUT either. That'll go to your terminal too. For instance:
$ perl -e "system 'ls -Fahl /dev/null/something' and die qq(fail: $! >> 8 == @{[$! >> 8]})"
ls: /dev/null/something: Not a directory
fail: 26205 >> 8 == 102 at -e line 1.
Upvotes: 0
Reputation: 80384
system($runCmd) or die("Failed to run \"$runCmd\": $!");
Unlike most Perl functions, system
returns false when it succeeds and true when it fails. This is completely backwards I know but it is just how it is.
You need "system() and" not "system() or". And you probably want $?
not $!
, although this can sometimes be tricky.
I have a minor aversion to
system(...) && die
because it screws up all the rest of || die
’s that normally make a continuous vertical double margin over on the right, I sometimes write
system(...) == 0 || die
so that they all line up correctly again.
Upvotes: 5
Reputation:
As perldoc -f die
says, die
doesn't give a specific exit code (only a nonzero exit code). To get what you want, you'll need something like:
my $exit_code=system($runCmd);
if($exit_code!=0)
{
print "Command $runCmd failed with an exit code of $exit_code.\n";
exit($exit_code >> 8);
}
else
{
print "Command $runCmd successful!\n";
}
Upvotes: 20