Perl: After a successful system call, "or die" command still ends script
Solution 1
That would be a little confusing, wouldn't? - Leonardo Herrera on Ikegami's answer
Yes, it is confusing that the system
command inverts true and false in Perl, and creates fun logic like this:
if ( system qw($command) ) {
die qq(Aw... If failed);
}
else {
say qq(Hooray! It worked!);
}
But, it's understandable why the system
command does this. In Unix, an exit status of zero means the program worked, and a non-zero status could give you information why your system
call failed. Maybe the program you were calling doesn't exist. Maybe the program sort of worked as expected. For example, grep
returns an exit code of 1
when grep
works, but there were no matching lines. You might want to distinguish when grep
returns zero, one, or a return code greater than one. (Yes, I know it's silly to use a system call to grep
in a Perl program, but that's the first example I could think of).
To prevent casual confusion, I create a variable that holds the exit status of my system
command instead of testing the output of system
directly:
my $error = system qw($command);
if ($error) {
die qq(Aw... It failed);
}
else {
say qq(Hooray! It worked!);
}
It's completely unnecessary, and people who work with Perl should know that system
reverses Perl's definition of true and false, but if I hadn't had my coffee in the morning, I may miss it as I go over someone else's code. Doing this little step just makes the program look a bit more logical.
The Perldoc of system give you code that allows you to test the output of your system command to see exactly what happened. (If there was an error, or a system interrupt signal killed the system
call). It's nice to know if you need to interrogate your system
return value to figure out what went wrong.
Solution 2
system
returns the exit status of the command it calls. In shell, zero exit status means success. You have to invert the logic:
0 == system qw(mkdir -p Purged) or die "Failed to create the dir\n";
Solution 3
system
returns 0
on success, so you want and
rather than or
.
See also: use autodie qw( system );
Solution 4
To add what hasn't been mentioned but what comes with it and can quietly mess things up.
The system
's return of 'zero-or-not' only tells whether the command itself executed successfully, by shell or execvp
; the 0
does not mean that the command succeeded in what it was doing.†
In case of non-zero return you need to unpack $?
for more information;† see system for how to do this. For one, the command's actual exit code is $? >> 8
, what the executed program was designed to communicate at its exit.
Altogether you may want to do something to the effect of
sub run_system {
my ($cmd, @other_agrs) = @_; # may do more via @other_args
my ($cmdref, $sys_ret) = (ref $cmd, 0); # LIST or scalar invocation?
if ($cmdref eq 'ARRAY') {
$sys_ret = system(@$cmd);
}
elsif (not $cmdref) {
$sys_ret = system($cmd);
}
else { Carp::carp "Invocation error, got $cmdref" }
return 1 if $sys_ret == 0;
# Still here? The rest is error handling.
# (Or handling of particular non-zero returns; see text footnote)
Carp::carp "Trouble with 'system($cmd)': $?";
print "Got exit " . ($? >> 8) . " from $cmd\n";
return 0; # or Carp::croak (but then design changes)
}
This can be done merely as system == 0 or do { ... };
or such right in the code, but this way there's a little library, where we can refine error handling more easily, eventually decide to switch to a module to manage external commands, etc. Also, being a user-level facility now it makes sense that it returns true on success (we aren't changing system
's design).
Additionally in this case, mkdir -p
is meant to be quiet and it may not say anything when it cannot do its thing, in some cases. This is by design of course, but one should be aware of it.
† A program may return whatever it wants; nothing requires of it to return zero if it succeeded (completed without error) and it does not have to follow any conventions; what it returns is entirely up to its design.
Typical Unix programs generally follow the convention of returning non-zero only if there were problems, but some do return non-zero values merely to indicate a particular condition. See for example here and here.
One example: for at least some versions, zip
returns non-zero (12) when it "has nothing to do" (nothing to update in a package for example), even though it completes successfully; so we get 3072 from system
in that case (when that 12 is packed into high bits of a number which is the return code). This is documented on its man page.
A zero return, though, normally does imply a successful operation, what is the main point here.
FintleWoodle
Updated on June 16, 2022Comments
-
FintleWoodle almost 2 years
I am using the following line to make a simple system call which works:
system ("mkdir -p Purged") or die "Failed to mkdir." ;
Executing the script does make the system call and I can find a directory called Purged, but the error message is still printed and the script dies. What is wrong with my syntax?
-
crazy_in_love about 10 yearsThat would be a little confusing, wouldn't?
-
Plaute about 5 yearsThat's fine! Simple code is always the best solution :)
-
brian d foy over 3 yearssystem doesn't really reverse anything, and that's not a good way to think about it. The system command returns something and you should understand the exit values of the program and know what to check. The
system
does not return a value that indicates if the command worked, and some are "successful" but have non-zero exit values. -
brian d foy over 3 yearsThis isn't really true.
system
returns the exit value of the program. In Unix, most of the time, programs return 0 to mean success and non-zero for other things. However, some programs use non-zero exit statuses to mean other types of success.system
is not judging or interpreting: it's passing through. -
zdim over 3 yearsEdited to state that some programs may return non-zero even if they ran without error, merely to indicate a particular condition: added a footnote, a parenthetical remark, and a line of comment in code. This does not change the point of this answer at all.