I want to take decision based on result of system - external commend's output.
like: In my code I am executing one system command and if that command gives error then I have to do something else something different.
Please help me to achieve this:
This is just algorithm ....... i am not able to get value from result
$result = `dt add $dest_file -c porting`;
if($result = 'error')
{
do something 1
}
else
{
do something 2
}
please suggest me way to retrieve value in $ result
I think in this case I should not use system
We have to know what the dt command is. I'm assuming it's DITrack which behaves in a nice Unixish way which means it returns a non-zero error code when it fails.
When Perl executes a system command, it returns the exit value (sort of...) in $?. If dt is DiTrack, it's a standardly implemented such Unix command, and returns a zero exit code when it works, and returns a non-zero exit code when it doesn't work, you can use the $? to determine if it succeeded or not:
$result = qx(dt add $dest_file -c porting); # qx(...) preferred over back ticks.
if ( $? != 0 ) {
chomp $result;
say qq(Everything is okay! Program returned $result);
}
else {
die qq(Some sort of error has happened);
}
The problem is that $? returns both the exit code, and mode of failure. There's this snippet of code from the system that parses $?:
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;
}
You might consider using regular expressions:
$result = `dt add $dest_file -c porting`;
if($result =~ /error/)
{
do something 1
}
else
{
do something 2
}
One thing to keep mind is that backticks typically/potentially returns the newline character as well so you need to account for this. Using regex will allow you to get around this quickly.
Related
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 …
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.
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.
I'm a beginner in Perl and I have some trouble using the "system" call. Here is a little piece of code where I try to execute 2 shell commands :
# First command is :
# dot -Tpng $dottmpfile > $pngfile
# Second command is :
# rm $dottmpfile
if (!($pngfile eq "")) {
my #args = ("dot", "-Tpng", $dottmpfile, " > ", $pngfile);
system (join (' ' , #args ))
or die "system #args failed : $!";
unlink $dottmpfile;
}
EDIT : Here is my code now, and I still get an error :
system dot -Tpng toto.dot > toto.png failed : Inappropriate ioctl for device at /home/claferri/bin/fractal.pl line 79.
I've used system to produce this piece of code.
Looking at perldoc -f system, note:
If there is more than one argument in LIST, or if LIST is an array with more than one value, starts the program given by the first element of the list with arguments given by the rest of the list. If there is only one scalar argument, the argument is checked for shell metacharacters, and if there are any, the entire argument is passed to the system's command shell for parsing
You are invoking system LIST so the > ends up being passed to dot instead of being interpreted by the shell.
I would recommend that you keep using system LIST because it is a little safer than passing everything through the shell. According to the docs, you can specify the output file by using the -o option to dot, so do that.
If you really want to dot your is and cross your ts (pun not intended), then you can use:
if ( defined $pngfile and $pngfile ne '') {
my #args = (dot => '-Tpng', $dottmpfile, "-o$pngfile");
if ( system #args ) {
warn "'system #args' failed\n";
my $reason = $?;
if ( $reason == -1 ) {
die "Failed to execute: $!";
}
elsif ( $reason & 0x7f ) {
die sprintf(
'child died with signal %d, %s coredump',
($reason & 0x7f),
($reason & 0x80) ? 'with' : 'without'
);
}
else {
die sprintf('child exited with value %d', $reason >> 8);
}
}
warn "'system #args' executed successfully\n";
unlink $dottmpfile;
}
You are using > to tell the shell to redirect output to a file yet by using invoking system LIST, you are bypassing the shell. Therefore, you can use:
system ( join (' ' , #args ) );
or
system "#args";
system returns 0 on success and non-zero on "failure". It's contrary to the way most of these idioms look and a little counter-intuitive, but with system calls you should use an expression like:
system($command) and warn "system $command: failed $?\n"; # and not or
or
if (system($command) != 0) { ... handle error ... }
Is the "dot" executable in the PATH? Does it have executable permissions? Which specific error are you getting with this code?
It seems that is correct according to perldoc -f system.
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;