Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:
I tend to use the system() function pretty often, and I want to make sure I detect any problems. So, I think this means I want to make sure that programs I run always have an exit status of 0.
My understanding of the system function is that it returns a 16-bit value, where the high 8 bits are the exit status, and the low 8 are the signal id. Is that correct?
It also seems that I don't actually have to save the exit status after every use of system(), since the value also gets stored in $?.
I don't expect the programs I'm running to be killed by any signals (unless I'm the one doing the sending) ... So, I think this means I'm only concerned that the high 8 bits are all off. Therefore, does this mean I should be carefully making my system calls like this:
system('some_shell_command') >> 8 and die "Failed!";
# or else (same thing),
system('some_program');
if ($? >> 8 != 0) {
die "Failed!";
}
or would the following suffice? :
system('foobar') == 0 or die "Failed!";
# or equivalently
system('foobar');
if ($? != 0) {
die "Failed!";
}
Also, I notice in the docs for system(), there's this:
if ($? == -1) {
print "failed to execute: $!\n";
}
elsif ($? & 127) {
printf "child died with signal %d, %s coredump\n",
($? & 127), ($? & 128) ? 'with' : 'without';
}
else {
printf "child exited with value %d\n", $? >> 8;
}
For the if expression, if the exit status is 16 bits, then the value goes from 0 to 655535. What does it mean to check if it's -1?
For the elsif expression, why are they bitwise anding it with 127? 127 in binary is 0b0111_1111. So, that's only giving me the bottom 7 bits of $?, not 8. Why throw away that top bit in the bottom byte?
Re: return value from system call, exit status, shift right 8, bitwise and, $?
by ikegami (Patriarch) on Feb 11, 2010 at 09:10 UTC
|
and the low 8 are the signal id. Is that correct?
No, the low 7 bits indicate the signal that ended the process. Bit 7 indicates whether a core dump was saved.
What does it mean to check if it's -1?
system itself encountered an error. For example, system will return -1 and set $! if the program to execute is not found.
or would [system(...) == 0 or die "Failed!";] suffice?
It can also be written as
system(...)
and die "Failed!";
It will die on any error, but not saying what failed and not saying why it failed seems insufficient to me. The user should at least be told what failed for an error that's not a programming error; a line number doesn't cut it.
Now, detecting whether a code dump occurred or not is going overboard.
This is pretty minimal:
sub _system {
my $prog = shift;
my $rv = system( { $prog } $prog => @_ );
if ($rv == -1) { die("Can't launch $prog: $!\n"); }
elsif (my $s = $rv & 127) { die("$prog died from signal $s\n"); }
elsif (my $e = $rv >> 8) { die("$prog exited with code $e\n"); }
}
| [reply] [d/l] [select] |
|
Ok. I think I see now. If the system() call returns "-1", this is the same as a binary number of all 1's (since the system presumably uses the two's-complement representation), and it looks like it is:
printf '%b', -1;
print "\n";
# Output is:
# 11111111111111111111111111111111
So, if we get -1 back, then I can just stop checking bits right there.
If the system() call does not return -1, but returns anything at all in those first 7 bits, then -- again -- I can stop checking anything else, because if those first 7 bits contain anything, that indicates that the program that was called received a signal and that's why it ended.
Finally, if the system() call does not return -1, and those first 7 bits are all zeros, then only at that point do those next 8 bits contain anything of interest (the exit status of the program called).
And to wrap this all up nicely, if the system() call returns 0, then that means all bits are off, thus:
- the value is not -1, so the program it called at least launched,
- the lower 8 bits are all zeros, so there was no signal caught (nor was a core dump saved),
- the higher 8 bits are all zeros, so exit status was zero too.
and therefore the program that system() called evidently ran successfully.
Thank you!
| [reply] [d/l] |
|
| [reply] |
|
No typo. The 8th bit is bit 7. They're numbered from 0
| [reply] |
Re: return value from system call, exit status, shift right 8, bitwise and, $?
by 7stud (Deacon) on Feb 11, 2010 at 06:10 UTC
|
For the if expression, if the exit status is 16 bits, then the value goes from 0 to 655535. What does it mean to check if it's -1?
-1 is not an 'exit status' of a system() call. -1 means system() was unable to execute the specified command. Because the command never executed, it did not generate an exit status.
EDIT: Apparently the -1 return value has some twists to it. The perl system() docs say that system() returns the return value of wait(). The perl wait() docs say:
Behaves like the wait(2) system call on your system: it waits
for a child process to terminate and returns the pid of the
deceased process, or "−1" if there are no child processes. The
status is returned in $? and "${^CHILD_ERROR_NATIVE}". Note
that a return value of "−1" could mean that child processes are
being automatically reaped, as described in perlipc.
The perlipc docs say:
On most Unix platforms, the CHLD (sometimes also known as CLD ) signal has special behavior with respect to a value of 'IGNORE' ....
Calling wait() with $SIG{CHLD} set to 'IGNORE' usually returns -1 on such platforms.
As far as I can tell, if you set $SIG{CHLD} = 'IGNORE', then you have told perl you don't want to wait for any children to finish executing. In that case, system() executes the command, but system() immediately returns -1 because your program is too impatient to wait and get the exit status from the child.
Therefore, system() can return -1 when system() successfully started the command.
For the elsif expression, why are they bitwise anding it with 127? 127 in binary is 0b0111_1111
That checks whether any of the first 7 bits is 'on'.
So, that's only giving me the bottom 7 bits of $?, not 8. Why throw away that top bit in the bottom byte?
Presumably, the agreed upon protocol is to use only the first 7 bits to indicate which signal terminated the program. In the old ascii system, the maximum number of characters that could be represented by one byte was 128 (0-127) because the 8th bit was used for other purposes. Maybe that is why the protocol uses only the first 7 bits to indicate which signal terminated the process.
In addition, I can imagine a system where there were only 2 possible signals. With that protocol, you would only need to write ($? & 3) to determine if a signal terminated the process. Maybe there were only 7 signals when the protocol was decided upon?
| [reply] |
|
| [reply] [d/l] |
Re: return value from system call, exit status, shift right 8, bitwise and, $?
by ikegami (Patriarch) on Feb 11, 2010 at 09:22 UTC
|
use IPC::System::Simple qw( systemx );
systemx( perl => ( '-e', 'print "foo"' )); # Dies on error
| [reply] [d/l] |
Re: return value from system call, exit status, shift right 8, bitwise and, $?
by 7stud (Deacon) on Feb 11, 2010 at 07:23 UTC
|
or would the following suffice? :
system('foobar') == 0 or die "Failed!";
Because I hate bit twiddling, that would be my choice.
Also, as "Learning Perl(5th)" says in a footnote on p. 236:
The return value of system is the "child exit code multiplied by 256, plus 128 if core was dumped, plus signal number triggering termination, if any." So you can do some regular math to get any additional information you need as well.
| [reply] [d/l] |
Re: return value from system call, exit status, shift right 8, bitwise and, $?
by rovf (Priest) on Feb 11, 2010 at 09:34 UTC
|
| [reply] [d/l] |
|
| [reply] |
|
| [reply] [d/l] [select] |
Re: return value from system call, exit status, shift right 8, bitwise and, $?
by bot403 (Beadle) on Feb 11, 2010 at 15:24 UTC
|
Others have covered your original question pretty well so I'll just chip in that I'm a fan of this construct in my code to get both the output and the RC.
This doesn't quite cover all the bases but can be expanded to do so.
eval { open P,"$cmd 2>&1 |"; };
if($@) { die('Cant launch cmd') }
my @output=<P>;
close P;
my $rc = $? >> 8;
| [reply] [d/l] |
Re: return value from system call, exit status, shift right 8, bitwise and, $?
by shawnhcorey (Friar) on Feb 11, 2010 at 14:15 UTC
|
system() is one of those weird commands that do different things depending on their arguments. It you use a single string argument, the return status is the status of the shell (or CMD.EXE on Windows). Most often, this is zero (success) even if the shell could not find a program to run. If you use a list of arguments, then you get the return status of the program but none of the argument are interpoled. This may be a good thing or a bad thing, depending on what you expect.
Example: a single string argument:
my $status = system( "foobar arg1 arg2 arg3" );
$status contains the status of the shell or CMD.EXE.
Example: a list of arguments:
my $status = system( "foobar", "arg1", "arg2", "arg3" );
$status contains the status of foobar. | [reply] [d/l] [select] |
Re: return value from system call, exit status, shift right 8, bitwise and, $?
by 7stud (Deacon) on Feb 11, 2010 at 14:54 UTC
|
system() is one of those weird commands that do different things depending on their arguments. It you use a single string argument, the return status is the status of the shell (or CMD.EXE on Windows). Most often, this is zero (success) even if the shell could not find a program to run.
I'm not seeing that:
use strict;
use warnings;
use 5.010;
my $status = system( "foobar arg1 arg2 arg3" );
say $status;
--output:--
Can't exec "foobar": No such file or directory at 2perl.pl line 5.
-1
os x 10.4.11 | [reply] [d/l] |
|
| [reply] |
Re: return value from system call, exit status, shift right 8, bitwise and, $?
by Anonymous Monk on Feb 11, 2010 at 04:08 UTC
|
if the exit status is 16 bits, then the value goes from 0 to 655535.
Sorry, typo. s/655535/65535/
| [reply] |
|
|