22

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.

5 Answers 5

20

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";
}
Sign up to request clarification or add additional context in comments.

7 Comments

The value returned from system() is an integer which encodes the exit value of the child process plus flags indicating how the child process exited (normal termination, killed by a signal, etc.) The value passed to exit() is just the exit value.
Right, the OP wants "Perl to execute a shell script and exit with the same code the shell script exits", and hence passing $exit_code to exit() makes the program exit with the same exit code as the exit code generated by passing $runCmd to the shell.
The program /bin/false exits with exit value 1. But perl -e 'print system("/bin/false")' prints 256.
@user5402: Not necessarily. From the info: "Portable programs should not assume that the exit status of `false' is 1, as it is greater than 1 on some non-GNU hosts." And we all want portable programs, don't we? :)
@musicKk I was just pointing out that the value returned by system() is 256 times the value you pass into exit().
|
13

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.

2 Comments

Just an addition: You have to shift the return value of system by eight to the right to get to the return value of $cmd. (perldoc.perl.org/functions/system.html)
Perl's system does not return 0 on success. It returns the value that the process used as the exit code (with more flags in the high byte). Although returning 0 for success is a unix convention, there are plenty of commands that use non-zero to distinguish different sorts of successful runs. You have to know what you expect based on the program you call, not some rule about perl.
5
  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.

1 Comment

Well, it returns whatever $runCmd returns. Some commands don't return 0 to signal success, and some non-zero exit values might mean success for whatever you are trying to do. Many unix tools do follow the convention of returning 0 for success, but what many tools do doesn't matter when you are using one that is different.
2

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);

2 Comments

255 doesn't mean that you failed to run the command (that's your -1 case). It means the command ran and returned 255 for whatever reason that command decided to return that. Note how your if differs from the one is the system docs.
Note that system($runCmd, @args) is not guaranteed to avoid the shell. If @args is empty, it's still system with a single argument.
0

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.

1 Comment

Don't check $err for true or false. Check it for the value that you expect from that command. system doesn't decide if that value is good or bad.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.