system() returns bad exit code - perl

I am using system to call an external application and I need to interpret its exit code.
I know that system returns the exit code from the command multiplied by 256, but when in foo.bat I write exit 256 the result is zero.
Why is this happening?

Windows uses 32-bit exit codes, so exit 256 is perfectly valid.
>cmd /c exit 256
>echo %ERRORLEVEL%
256
However, Perl only keeps the least significant 8 bits.
>perl -e"system 'exit 256'; CORE::say $?>>8"
0
>perl -e"system 'exit 266'; CORE::say $?>>8"
10
This is a Perl defect for which there's no good reason. If you use Win32::Process instead of system, you can obtain the correct exit code.
>perl -MWin32::Process=NORMAL_PRIORITY_CLASS,INFINITE -e"Win32::Process::Create(my $proc, $ENV{COMSPEC}, 'cmd /c exit 256', 0, NORMAL_PRIORITY_CLASS, '.') or die $^E; $proc->Wait(INFINITE); $proc->GetExitCode(my $exit_code); CORE::say $exit_code;"
256

The return code is a single-byte value from zero to 255.
The most reliable way to check the status of a system call is to examine $?. It's documented in perldoc perlfunc like 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;
}

All the possibilities of failure can be obtained by examining $?.
$exit_val = $? >> 8;
$signal = $? & 127;
$dumped_core = $? & 128;`
To quote from perldoc perlvar :
$CHILD_ERROR
$?
The status returned by the last pipe close, backtick (``) command, successful call to wait() or waitpid(), or from the system() operator. This is just the 16-bit status word returned by the traditional Unix wait() system call (or else is made up to look like it). Thus, the exit value of the subprocess is really ($? >> 8 ), and $? & 127 gives which signal, if any, the process died from, and $? & 128 reports whether there was a core dump.

Related

in perl, how do we detect a segmentation fault in an external command

Following is C code that is destined to crash:
#include<stdio.h>
#include<stdlib.h>
int main() {
char *p = NULL;
printf("Value at P: %c\n", *p);
return 0;
}
When I compile and run it (RH4 machine with gcc 4.5.2), it predictably gives a segmentation fault:
% ./a.out
Segmentation fault
% echo $status
139
If I run it with Perl v5.8.5, this happens:
% perl -e 'system("./a.out") and die "Status: $?"'
Status: 11 at -e line 1.
The perlvar documentation for $? says that
Thus, the exit value of the subprocess is really ($?>> 8 ), and $? &
127 gives which signal, if any, the process died from, and $? & 128
reports whether there was a core dump.
11 >> 8 is 0, and 11 & 127 is 11.
Why the different exit statuses? If we cannot depend on the exit status, what should be the way to detect segmentation fault in an external command?
Reading the documentation for system might answer your question:
system('a.out');
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;
}
Output:
child died with signal 11, without coredump
The shell just encodes the signal in the status in a different way: 139 - 128 = 11. For example, man bash says:
The return value of a simple command is its exit status, or 128+n if the command is terminated by signal n.
The OS always sees the same thing... what is returned by the wait(2) family of functions. system(3), et. all, call wait(2). How it peculates up you is what is causing the differences. The shell and the programs do different things and report different ways. Having to shift right 8 to get the most common exit status would be very annoying in shell programs and confuse less tech savvy users.
While the very first UNIX systems I used (genuine Unix) had the same returns I have always wondered if pre-release versions were different and returning the signal and core/dump were a later addition.
My preferred reporting of exit status tends to have the computer split my bits for me.
my $x = $?; #save status
print( "Exit status: %d.%d%s\n",
$x>>8, $x&127, ($x&128)?"*":"" );

Perl: Capturing correct return value from 'system' command

I'm a beginner in Perl. I have a Windows batch script which contains multiple NMake commands. An existing issue with this batch script is that even if the NMake command fails during its execution, ERRORLEVEL doesn't get set properly.
So we never know whether the command worked until we parse the log file. I looked into it but couldn't find a solution. I, then thought of converting this batch script to a Perl script assuming that trapping error will be easier but it seems it's not that easy :)
Whenever I run my Perl script, the 'system' command always returns 0. I looked at many different links, and realized that capturing the correct return status of 'system' command is not that straightforward. Still, I tried the suggestions but things are not working. :(
Let me mention that the NMake command that is called, in turn, calls many different commands during its execution. For instance, the command output mentioned below, which is throwing 'fatal error', is actually part of a Perl script (check_dir.pl). This call to Perl script is written in the NMake file itself.
If I call this Perl file (check_dir.pl) directly and check for exit value, I get correct result i.e., the command fails and prints a non-zero exit value (...unexpectedly returned exit value 2).
Tried Perl's system function but it didn't help. I used the following code:
system ("nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1");
if ( $? == -1 ) {
print "Command failed to execute: $!\n";
}
elsif ( $? & 127 ) {
printf "The child died with signal %d, %s a coredump\n",
( $? & 127 ), ( $? & 128 ) ? 'with' : 'without';
}
else {
printf "child exited with value %d\n", $? >> 8;
}
Output:
.....
.....
Unable to open dir: R:\TSM_Latest
Compressing...NMAKE : fatal error U1077: 'if' : return code '0x2'
Stop.
child exited with value 0
Also tried:
use IPC::System::Simple qw(system);
my $exit_status = system ("nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1");
if ($exit_status != 0) {
print "Failure";
exit 3;
} else {
print "Success";
}
Finally tried the following module:
use IPC::Run qw( run timeout );
run "nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1" or die "NMake returned $?";
Nothing seems to be working :(
Please correct me if i'm interpreting the return value of system incorrectly.
You have:
use IPC::System::Simple qw(system);
my $exit_status = system ("nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1");
Given that you don't seem to care about the actual output, you can try
my $exit_status = systemx(nmake =>
qw(/f _nt.mak pack_cd),
"SUB_PLAT=$PLAT",
"DR=$plat",
);
To make sure you bypass cmd.exe and see if you get something useful.
For reference, the exit codes from nmake are listed here.
Running the following program:
use strict; use warnings;
use IPC::System::Simple qw(systemx);
use Try::Tiny;
my $status = 0;
try { systemx nmake => qw(/f bogus) }
catch { ($status) = ( /exit value ([0-9])/ ) };
print "Failed to execute nmake. Exit status = $status\n";
produces:
NMAKE : fatal error U1052: file 'bogus' not found
Stop.
Failed to execute nmake. Exit status = 2
The following version:
use strict; use warnings;
my $status = system nmake => qw(/f bogus);
if ($status) {
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;
}
}
produces:
NMAKE : fatal error U1052: file 'bogus' not found
Stop.
child exited with value 2
In fact, even when I use
my $status = system "nmake /f bogus";
I get the same correct and expected output.
Ditto when I use
my $status = system "nmake /f bogus 2>&1";
These observations lead me to the following questions:
Which version of nmake are you using?
Is the /I option in effect? Even though you don't set it from the command line, note the following:
/I Ignores exit codes from all commands. To set or clear /I for part of a makefile, use !CMDSWITCHES. To ignore exit codes for part of a makefile, use a dash (–) command modifier or .IGNORE. Overrides /K if both are specified.
So, I put together the following files:
C:\temp> cat test.mak
test.target: bogus.pl; perl bogus.pl
C:\temp> cat bogus.pl
exit 1;
And, ran:
use strict; use warnings;
my $status = system "nmake /f test.mak 2>&1";
if ($status) {
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;
}
}
which gave me the output:
perl bogus.pl
NMAKE : fatal error U1077: 'c:\opt\perl\bin\perl.EXE' : return code '0x1'
Stop.
child exited with value 2
where the last line shows that the exit status of nmake was correctly propagated.
Conclusion:
You have some other problem.
In fact, the OP later pointed out in comments that:
The actual command that i am trying to run is: system ("nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1 | C:\\tee2 $TEMP_DIR\\modules-nt_${platlogfile}");
Given tees involvement in the pipeline, it is not surprising that nmakes exit code gets lost. tee is successfully able to process output from nmake, so it returns success, and that's the exit code your script sees.
Therefore, the solution is to capture the output of nmake yourself, either using qx (coupled with the appropriate level of error checking), or using capture from IPC::System::Simple. Then, you can decide to whether you want to print that output, save to a file, put it in an email etc …

Perl - $? shows 0 for a -1 return code on Windows 2008 64bit

Using Strawberry Perl 5.12.3
Running manually:
E:\informatica\tools>infacmd isp ping -sn tt -re 0
[ICMD_10033] Command [ping] failed with error [[INFACMD_10053] Service [tt] Domain [dmt3-9-dom-poc] has failed to ping back.].
E:\informatica\tools>echo %ERRORLEVEL%
-1
When I run the same command through Perl's "system", the $? shows 0. Perl code:
use strict;
use warnings;
my $cmd = system("infacmd isp ping -sn tt -re 0");
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;
}
Output:
[ICMD_10033] Command [ping] failed with error [[INFACMD_10053] Service [tt] Domain [dmt3-9-dom-poc] has failed to ping back.].
child exited with value 0
The same on Windows 2003 32bit and ActiveState Perl 5.8.8 shows correct results.
You might have better luck with ${^CHILD_ERROR_NATIVE} than with the emulation of unix's process status structure.
I believe that your manual test is not entirely sufficient, as the "errorlevel" variable can be shadowed or matters can otherwise be confused by usage of the shell, so your "infacmd" may not be exiting with the exit code that you think it is.
Your perl script is invoking this subprocess via the shell. Does the behavior change if you invoke it directly instead? (which is usually good practice...)
i.e. if you change the system line to this:
my $cmd = system('infacmd', 'isp', 'ping', '-sn', 'tt', '-re', '0');
... is the behavior affected at all?
I think you should shift $? (I.e., divide it by 256) before doing the branch...
$res = $? >>8;
if ($res == -1)
....
According to system definition in perlfunc
EDIT: I have just read in perl 5 by examples (chapter: handling errors and signals) that on windows you cannot rely on $? To represent the exit state of pipes and system. this could well your case.
the suggestion is,then, to capture the command output and parse it to find out which is the error code...
my $output = `mycmd 2>&1`;
if ($output =~ /.....
That leaves Win32::Process. If the exit code from this is zero, the exit code is zero.

perl invokes shell-- interrupt ^C stops the shell, not the perl

I want to use a Perl script to batch a repetitive operation which is invoked with system(). When something goes wrong and I want to interrupt this script, the ^C is captured by the shell, stopping whatever job, and the Perl script goes merrily along to the next one.
Is there a way I can invoke the job so that an interrupt will stop the Perl script?
You can check $? to see whether the command executed by system died from signal 2 (INT):
Here's a full example of parsing $?:
my $rc=system("sleep 20");
my $q=$?;
if ($q == -1) {
print "failed to execute: $!\n"
} elsif ($? & 127) {
printf "child died with signal %d, %s coredump\n",
($q & 127), ($q & 128) ? 'with' : 'without';
} else {
printf "child exited with value %d\n", $q >> 8;
}
# Output when Ctrl-C is hit:
# child died with signal 2, without coredump
Therefore the exact check you want is:
my $rc=system("sleep 20");
my $q=$?;
if ($q != -1 && (($q & 127) == 2) && (!($? & 128))) {
# Drop the "$? & 128" if you want to include failures that generated coredump
print "Child process was interrupted by Ctrl-C\n";
}
References: perldoc system for $? handling and system() call; perldoc perlvar for more details on $?
You are not checking the return value of system. Add to your parent program:
use autodie qw(:all);
and it program will abort as expected:
"…" died to signal "INT" (2) at … line …
You may catch this exception with Try::Tiny in order to clean-up on your own or use a different message.

Why does my Perl script return a zero return code when I explicitly call exit with a non-zero parameter?

I have a Perl script which calls another script. The Perl script should be propagating the script's return code but seems to be returning zero to its caller (a Java application) desipte the explicit call to exit $scriptReturnCode.
Code and output as follows (I realise that <=> could/should be != but that's what I have):
print "INFO: Calling ${scriptDirectory}/${script} ${args}"
$scriptReturnCode = system("${scriptDirectory}/${script} ${args}");
if ( $scriptReturnCode <=> 0 ) {
print "ERROR: The script returned $scriptReturnCode\n";
exit $scriptReturnCode;
} else {
print "INFO: The script returned $scriptReturnCode.\n";
exit 0;
}
The output I have from my Java is:
20/04/2010 14:40:01 - INFO: Calling /path/to/script/script.ksh arg1 arg2
20/04/2010 14:40:01 - Could not find installer files <= this is from the script.ksh
20/04/2010 14:40:01 - ERROR: The script returned 256
20/04/2010 14:40:01 - Command Finished. Exit Code: 0 <= this is the Java app.
You need to shift the return code from system() call by 8 bits.
E.g. $exit_value = $? >> 8; # In your script the $? is $scriptReturnCode
From http://perldoc.perl.org/perlfaq8.html :
system() runs a command and returns exit status information (as a 16 bit value: the low 7 bits are the signal the process died from, if any, and the high 8 bits are the actual exit value
A more expanded code checking for coredumps as well could look like this:
system();
if ($? == -1) {
print "failed to execute: $!\n";
} elsif ($? & 127) {
printf "child died - signal %d, %s coredump\n",
($? & 127), ($? & 128) ? 'with' : 'without';
} else {
printf "child exited with value %d\n", $? >> 8;
}
UPDATE: As per ysth's excellent reminder, the exit codes are truncated at 8 (low) bits, so returning 256 instead of the intended 1 ends up as 0. Similarly, returning 257 ends up as 1.
If capturing $? and shifting its value is too much trouble to remember, you can simplify that code by using IPC::System::Simple, which enhances system() and backticks with more error checking and diagnostics, e.g.:
use IPC::System::Simple qw(run EXIT_ANY);
my $command = "${scriptDirectory}/${script} ${args}";
print "INFO: Calling $command\n";
# runs command through a shell first; does not die on any exit value
run(EXIT_ANY, $command);
my $scriptReturnCode = $IPC::System::Simple::EXITVAL;