I need to terminate the perl script according to the return code from the previous step.
something like
IF ERRORLEVEL 1 goto ERROR
in batch processing.
i have
$PROG = `spu_comp 2>&1 $a 1 1`;
i need if this step gives error, program should terminate.
thanks in advance for your inputs.
Immediately after the line where you assign to $PROG, add this line:
($? >> 8) and die "spu_comp exited with non-zero return value";
$ perl -le'`sh -c "exit 0"`;($?>>8) and die "exited with non-zero: ", ($?>>8)'
$ perl -le'`sh -c "exit 1"`;($?>>8) and die "exited with non-zero: ", ($?>>8)'
exited with non-zero: 1 at -e line 1.
It appears that ERRORLEVEL is not a true exit code to perl.
i have the same issue. A bat file of
#Echo OFF
echo setting error level 1
EXIT /B 1
With a perl file of
#!/usr/bin/perl
$command = `C:\foo.bat`;
print "Error Level: " .$? ."\n";
print "Command: " . $command . "\n";
Yields output of
Error Level: 0
Command:
A perl file of
#!/usr/bin/perl
my $command = `dir`;#try both dir and dri to test real exit codes against batch exit codes
print "Error Level: " .$? ."\n";
print "Command: " . $command . "\n";
will yield
C:\>back.pl
'dri' is not recognized as an internal or external command,
operable program or batch file.
Error Level: 256
Command:
C:\>back.pl
Error Level: 0
Command: Volume in drive C has no label.
Volume Serial Number is 8068-BE74
Directory of C:\
12/13/2010 11:02 AM 7 8
06/02/2010 01:13 PM 0 AUTOEXEC.BAT
06/04/2010 01:00 PM <DIR> AutoSGN
12/13/2010 12:03 PM 111 back.pl
06/02/2010 01:13 PM 0 CONFIG.SYS
06/03/2010 07:37 PM <DIR> Documents and Settings
12/13/2010 12:01 PM 46 foo.bat
06/04/2010 03:17 PM <DIR> HorizonTemp
06/02/2010 02:41 PM <DIR> Intel
06/04/2010 02:19 PM <DIR> league
06/04/2010 12:31 PM <DIR> Perl
12/10/2010 03:28 PM <DIR> Program Files
12/08/2010 04:13 PM <DIR> Quarantine
12/13/2010 08:14 AM <DIR> WINDOWS
5 File(s) 164 bytes
9 Dir(s) 18,949,783,552 bytes free
C:\>
You can get the correct return code from $PROG by adding the following line.
my $ret = $?/256 #/
or a cleaner way
my $ret = $? >> 8;
Then compare the $ret with the possible values you can retrieve
if ($ret == 0)
{
# Do something if finished successfully
}
elsif($ret == 1)
{
error();
}
else
{
# Return something else that was nor 0 nor 1
}
Further to #husker's answer, it's worth noting $? only works for codes of 255 or less. Windows error codes typically exceed this. The IPC::System::Simple module, however, provides methods like capture() that can correctly retrieve codes > 255.
e.g.
use Test::More;
use IPC::System::Simple qw(capture $EXITVAL EXIT_ANY);
my $modeTest = capture(EXIT_ANY, "some command that sets error code 5020");
is( $EXITVAL , 5020, "Expect error code 5020" );
Related
Why does the Perl file test operator "-l" fail to detect symlinks under the following conditions?
System Info
john#testbed-LT:/temp2/test$ uname -a
Linux Apophis-LT 4.13.0-37-generic #42-Ubuntu SMP Wed Mar 7 14:13:23 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
john#testbed-LT:/temp2/test$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 17.10
Release: 17.10
Codename: artful
Perl Info
john#testbed-LT:/temp2/test$ perl -v
This is perl 5, version 26, subversion 0 (v5.26.0) built for x86_64-linux-gnu-thread-multi (with 56 registered patches, see perl -V for more detail)
Test Resources
john#testbed-LT:/temp2/test$ touch regular_file
john#testbed-LT:/temp2/test$ mkdir dir
john#testbed-LT:/temp2/test$ ln -s regular_file symlink
john#testbed-LT:/temp2/test$ ls -al
total 12
drwxrwxr-x 3 john john 4096 May 6 02:29 .
drwxrwxrwx 6 john john 4096 May 6 02:29 ..
drwxrwxr-x 2 john john 4096 May 6 02:29 dir
-rw-rw-r-- 1 john john 0 May 6 02:29 regular_file
lrwxrwxrwx 1 john john 12 May 6 02:29 symlink -> regular_file
Script Containing Failing "-l" Operator
john#testbed-LT:/temp2/test$ cat ~/.scripts/test.pl
#!/usr/bin/perl
use strict;
use warnings;
use Cwd 'abs_path';
my $targetDir = "/temp2/test";
opendir(DIR, $targetDir) || die "Can't open $targetDir: $!";
while (readdir DIR) {
my $file = "$_";
if($file =~ m/^\.{1,2}/) {
next;
}
$file = abs_path($file);
if(-l "$file") {
print "Link: $file\n";
}
elsif(-d "$file") {
print "Dir: $file\n";
}
elsif(-f "$file") {
print "File: $file\n";
}
else {
print "\n\n *** Unhandled file type for file [$file]!\n\n";
exit 1;
}
}
closedir(DIR);
Script Output
john#testbed-LT:/temp2/test$ perl ~/.scripts/test.pl
File: /temp2/test/regular_file
Dir: /temp2/test/dir
File: /temp2/test/regular_file
Problem I'm Trying to Solve
Note in the above output that the symlink (named "symlink") is not listed while the file, "regular_file," is listed twice (I want "symlink" listed -- the actual link and not the file it points to).
When I change ... if(-l "$file") ... to ... if(lstat "$file") ... in the script, again "symlink" is not listed while "regular_file" is listed twice, but they are being listed from within the block meant to catch symlinks, i.e.:
john#testbed-LT:/temp2/test$ perl ~/.scripts/test.pl
Link: /temp2/test/regular_file
Link: /temp2/test/dir
Link: /temp2/test/regular_file
Goal
The output I'm trying to achieve (which is faked below -- not actually generated by the script, but by hand) is:
john#testbed-LT:/temp2/test$ perl ~/.scripts/test.pl
File: /temp2/test/regular_file
Dir: /temp2/test/dir
Link: /temp2/test/symlink
...but not necessarily in that order (I don't care about the order of the listing).
Why is the above-shown script not achieving the above-stated goal (why is the "-l" operator not working)?
perldoc Cwd:
abs_path
my $abs_path = abs_path($file);
Uses the same algorithm as getcwd(). Symbolic links and relative-path components ("." and "..") are resolved to return the canonical pathname, just like realpath(3). On error returns undef, with $! set to indicate the error.
(Emphasis mine.)
If you want to see symlinks, don't use abs_path.
What you want to do instead is
$file = "$targetDir/$file";
i.e. prepend the name of the directory you read $file from.
Additional notes:
opendir(DIR, $targetDir) || die "Can't open $targetDir: $!";
while (readdir DIR) {
my $file = "$_";
should be
opendir(my $dh, $targetDir) || die "Can't open $targetDir: $!";
while (my $file = readdir $dh) {
Why use bareword filehandles when you can just use normal variables (that are scoped properly)?
There's no reason to quote "$_" here.
Why first assign to $_ when you're just going to copy the string to $file in the next step?
Note in the above output that the symlink (named "symlink") is not listed while the file, "regular_file," is listed twice
Yeah, because you used abs_path to turn symlink into /temp2/test/regular_file. Get rid of that line.
By the way, you are missing
$file = "$targetDir/$file";
The only reason your program worked without it is because $targetDir happened to be the current work directory.
I want to get the list of file names present in the remote location.
I am using the below snippet in my Perl script.
my $command = "sftp -q -o${transferAuthMode}=yes -oPort=$sftpPort ${remoteUsername}\#${remoteHost} 2>\&1 <<EOF\n" .
"cd \"${remotePath}\"\n" .
"ls -l \n" .
"quit\n" .
"EOF\n";
my #files = `$command`;
When the number of files in the remote location is large (>500) then not all the file names are captured in #files.
When I manually do SFTP and list the files, all files are getting listed but I'm not getting the same through the script. Each time getting #files size different. It's occurring only when there are large number of files.
I'm unable find the reason behind this. Could you please help?
This can be achieved without requiring any additional package module/s. I tested this on my CentOS 7 Server (Windows VM).
My remote host details: I got ~2000 files in the remote host dir. A CentOS 6.8 server.
%_gaurav#[remotehost]:/home/gaurav/files/test> ls -lrth|head -3;echo;ls -lrth|tail -2
total 7.9M
-rw-rw-r--. 1 gaurav gaurav 35 Feb 16 23:51 File-0.txt
-rw-rw-r--. 1 gaurav gaurav 35 Feb 16 23:51 File-1.txt
-rw-rw-r--. 1 gaurav gaurav 38 Feb 16 23:51 File-1998.txt
-rw-rw-r--. 1 gaurav gaurav 38 Feb 16 23:51 File-1999.txt
%_gaurav#[remotehost]: /home/gaurav/files/test>
Script output from LocalHost: Please note that I am running your command sans the o${transferAuthMode}=yes part. As seen below, the script is able to gather all results in an array, greater than 500 results.
I am prnting the total entries, some particular index numbers from the array to show the results, but give it a try with un-commented Dumper line to see the full result.
%_STATION#gaurav * /root/ga/study/pl> ./scp.pl
Read 2003 lines from SCP command.
ArrayIndex: 2,3,1999,2000 contain:
[-rw-rw-r-- 0 501 501 36B Feb 16 23:51 File-58.txt]
[-rw-rw-r-- 0 501 501 37B Feb 16 23:51 File-129.txt]
[-rw-rw-r-- 0 501 501 38B Feb 16 23:51 File-1759.txt]
[-rw-rw-r-- 0 501 501 38B Feb 16 23:51 File-1810.txt]
%_STATION#gaurav * /root/ga/study/pl>
Script and its Working:
#!/usr/bin/perl
use strict ;
use warnings ;
use Data::Dumper ;
my $sftp_port=22 ;
my ($user, $host) = ("gaurav","192.168.246.137") ;
my $remote_path = '/home/gaurav/files/test' ;
my #result ; # To store result
my $command = "sftp -q -oPort=$sftp_port ${user}\#${host} 2>\&1 <<EOF\n"."cd $remote_path\nls -lrth\nquit\nEOF" ;
# open the command as a file handle, read output and store it.
open FH, "$command |" or die "Something went wrong!!\n" ;
while (<FH>) {
tr/(?\r|\f|\n)//d ; # Removing any new line, carriage return or form feed.
push(#result,"\[$_\]") ;
}
close FH ;
#print Dumper #result ;
# Just for printing a little bit of results from
# the array. Following lines can be deleted.
my $total = scalar #result ;
print "Read $total lines from SCP command.\n" ;
print "\nArrayIndex: 2,3,1999,2000 contain:\n
$result[2]
$result[3]
$result[1999]
$result[2000]
" ;
Another way: One could also get around this issue by making a shell script and calling it from the perl script and read its output. As shown below, my shell script which gets called by the perl script and the final output. This can be used as a quick technique when one doesn't have much time to write/formulate commands in perl directly. You can use the qx style(shown below) in earlier script as well.
Shell script "scp.sh"
%_STATION#gaurav * /root/ga/study/pl> cat scp.sh
#!/bin/bash
sftp -oPort=${1} ${2}#${3} 2>&1 <<EOF
cd ${4}
ls -l
quit
EOF
Perl Script "2scp.pl"
%_STATION#gaurav * /root/ga/study/pl> cat 2scp.pl
#!/usr/bin/perl
use strict ;
use warnings ;
use Data::Dumper ;
my $sftp_port=22 ;
my ($user, $host) = ("gaurav","192.168.246.137") ;
my $remote_path = '/home/gaurav/files/test' ;
# Passing arguements to shell script using concatination.
my $command = './scp.sh '." $sftp_port $user $host $remote_path" ;
my #result = qx{$command} ; # Runs the command and stores the result.
my $total = scalar #result ;
print "Read $total lines from SCP command.\n" ;
# End.
Output:
%_STATION#gaurav * /root/ga/study/pl> ./2scp.pl
Read 2004 lines from SCP command.
%_STATION#gaurav * /root/ga/study/pl>
Try it out and let us know.
Thanks.
December 10, 2014
Can someone kindly help me to resolve this issue where character '>' causes the perl program to exit prematurely when run on a remote Windows server?
The actual output is:
K:\ Volume in drive K is DataDisk
Volume Serial Number is E8BD-C593
Directory of K:\
04/15/2011 05:25 AM <DIR
The expected output is:
K:\>dir
Volume in drive K is DataDisk
Volume Serial Number is E8BD-C593
Directory of K:\
12/08/2014 11:18 PM <DIR> ftpvol
04/15/2011 05:25 AM <DIR> Images
1 File(s) 0 bytes
16 Dir(s) 246,180,012,032 bytes free
Here is the script:
#!/usr/bin/perl
use Net::Telnet ();
my $node = $ARGV[0];
my $ipAddress = $ARGV[1];
my $username = $ARGV[2];
my $password = $ARGV[3];
my $mmlCommand0 = "hostname&prcstate -l";
my $filedate = `date +%Y%m%d`; #date in format YYYYMMDD
chomp($filedate); #deletes newline character at end
my $numArgs = $#ARGV + 1;
if($numArgs == 4){
my $telnet = new Net::Telnet( Host=>$ipAddress, Port=>23, Timeout=>20, Errmode=>'die', Prompt=>'/>/');
$telnet->open() or die "hai $telnet->errmsg ";
$telnet->waitfor('/login name:/');
$telnet->print($username);
$telnet->waitfor('/password:/');
$telnet->print($password);
$telnet->waitfor('/Windows NT Domain:/');
$telnet->print("");
$telnet->waitfor('/>/');
## get printouts
#print $telnet->cmd($mmlCommand0);
print $telnet->cmd("K:");
print $telnet->cmd("dir");
}
else{
print "\n!!! Correct syntax is: command <node> <IP address> \nExample: \n\n";
}
print "\n\n";
exit(0);
script does not execute if I remove prompt or try to set another prompt.
However I think the error that the character '>' is always interpreted as the prompt.
my $telnet = new Net::Telnet( Host=>$ipAddress, Port=>23, Timeout=>20, Errmode=>'die');
$telnet->prompt('/$/');
Thanks in advance!
December 11, 2014
A "reply" button would be nice to have instead of having to edit an original port...
I am not quite following what Mr Llama has suggested. Accordingly if I am using the functions print() and waitfor() the promt should NOT be used. In that case I removed prompt however the code still does not work. Could you be kind to post a working code sample that will retrieve characters '<' and '>' in the printout and not treat either as a DOS prompt?
The Net::Telnet documentation says that you only need to use the prompt attribute if you're not using print() and waitfor() for communication (it's meant to be used with login().
In your case, the prompt value is being removed from the response. Try setting the prompt value to something that will never occur and that should fix your issue. Do be careful in what value you select as the prompt value will be treated as a regular expression.
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 …
I have a script that has been running for over a year and now it is failing:
It is creating a command file:
open ( FTPFILE, ">get_list");
print FTPFILE "dir *.txt"\n";
print FTPFILE "quit\n";
close FTPFILE;
Then I run the system command:
$command = "ftp ".$Server." < get_list | grep \"\^-\" >new_list";
$code = system($command);
The logic the checks:
if ($code == 0) {
do stuff
} else {
log error
}
It is logging an error. When I print the $code variable, I am getting 256.
I used this command to parse the $? variable:
$exit_value = $? >> 8;
$signal_num = $? & 127;
$dumped_core = $? & 128;
print "Exit: $exit_value Sig: $signal_num Core: $dumped_core\n";
Results:
Exit: 1 Sig: 0 Core: 0
Thanks for any help/insight.
Mel - you might gain a bit more information by looking at standard error output of the ftp command.
1) Does the FTP command work by hand from shell prompt?
2) If command line ftp works, capture the output (stdout and stderr) of the ftp command and print it in Perl script. For a couple of ways to do so, see perlfaq8 - How can I capture STDERR from an external command?
The two easiest apporaches are these:
my $output = `$command 2>&1`;
my $pid = open(PH, "$command 2>&1 |");
while (<PH>) { print "Next line from FTP output: $_"; }
3) As wisely noted by Snake Plissken in a comment, an alternate (and more idiomatic and possibly easier) approach is to scrap the system call to "ftp" command and instead use Net::FTP Perl module.