luxAeterna has asked for the wisdom of the Perl Monks concerning the following question:

Oh Wisest ones

I am missing something when I try to capture the return code of an execution with back ticks using $?. As far as I know, $?>>8 should contain such return code, the rest of the bits containing the number of the interruption signal if any (of no interest for me in this case).

This is a Suse operating system.

When I execute my command inside the perl code, I capture a false return code zero:

my @flush = `$command`; my $cr=$?>>8; print "$command\n"; print "Returned $cr\n";

Gives as a result:

/soft/perl-5.16.3/bin/perl /users/iax00/exploit/script/iax_0taskchklog +.pl -m --timeout='10' --success-code='0' --script='/users/iax00/expl +oit/script/iax_0tasksimp.pl' --script-prefix='${UNXEXSCRIPT}/start /s +oft/perl-5.16.3/bin/perl' --script-params='-m -v 2 --config-file /use +rs/iax00/exploit/data/iaxfcverifTomcat_admin_portal.xml' Returned 0

But when I execute the command on the shell, the return code is 5:

[mzpiax71@yval3d80]$ /soft/perl-5.16.3/bin/perl /users/iax00/exploit/s +cript/iax_0taskchklog.pl -m --timeout='10' --success-code='0' --scri +pt='/users/iax00/exploit/script/iax_0tasksimp.pl' --script-prefix='${ +UNXEXSCRIPT}/start /soft/perl-5.16.3/bin/perl' --script-params='-m -v + 2 --config-file /users/iax00/exploit/data/iaxfcverifTomcat_admin_por +tal.xml' LOGGING STYLE 1: LOGFILE /users2/iax00/log/iax_0taskchklog_20170507.lo +g. Verbosity 0 INFO - TBE Debut du job iax_0taskchklog a 2017-05-07 15:31:33 INFO - TBE Execution du script /users/iax00/exploit/script/start /soft +/perl-5.16.3/bin/perl /users/iax00/exploit/script/iax_0tasksimp.pl -m + -v 2 --config-file /users/iax00/exploit/data/iaxfcverifTomcat_admin_ +portal.xml INFO - TBE L'execution du script a genere le code de retour 5 INFO - TBE Script /users/iax00/exploit/script/iax_0tasksimp.pl pas bie +n execute. INFO - TBE Fin du job iax_0taskchklog a 2017-05-07 15:31:35 [mzpiax71@yval3d80]$ echo $? 5

This is the first time I find this problem. But thou, oh Great PerlPath Finders, may have stumbled into it before so I humbly seek for your Wisdom.

Replies are listed 'Best First'.
Re: problem retrieving return code of an execution with back ticks (updated)
by haukex (Archbishop) on May 07, 2017 at 14:39 UTC

    You said you don't care about the other information in $?, but I'd say that's not a good way to look at it, since I assume you do want to know whether your script completed successfully or not. So you should always check $? against zero first, and then you can inspect its value to see what went wrong. See the example code in system (slightly reformatted for brevity):

    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 }

    Or, easier, use capture or (better) capturex from IPC::System::Simple, which has very good handling of these errors built in. I wrote about the topic of running external commands at length here.

    One more small thing is - have you inspected the command's output in @flush?

    There is also a small possibility that the shell that Perl is using to execute the backticks is different from your command line shell, so there could be a difference there, which is why I recommended capturex, which doesn't invoke the shell at all. The only thing in your command that seems to need the shell's capabilities is ${UNXEXSCRIPT}*, which you can do at the Perl level with $ENV{UNXEXSCRIPT}. What I mean is something like the following (untested). Note that I've made some assumptions about how the script parses command line arguments; if you have trouble with --script-prefix or --script-params you might need to fiddle with adding quoting to them:

    use IPC::System::Simple qw/capturex/; my @flush = capturex('/soft/perl-5.16.3/bin/perl', '/users/iax00/exploit/script/iax_0taskchklog.pl', '-m','--timeout=10','--success-code=0', '--script=/users/iax00/exploit/script/iax_0tasksimp.pl', '--script-prefix='.$ENV{UNXEXSCRIPT}.'/start /soft/perl-5.16.3/bin +/perl', '--script-params=-m -v 2 --config-file /users/iax00/exploit/data/i +axfcverifTomcat_admin_portal.xml');

    Without knowing anything about iax_0taskchklog.pl or iax_0tasksimp.pl (the latter is returning the error code, correct?), these are just guesses - it's also possible that one or both of these scripts have code in them that causes them to act differently when run from the shell vs. run from another script (environment variables, interactive prompting, ...), and that is causing the problem.

    * Update: Actually, I just noticed you said '${UNXEXSCRIPT}...', so I guess you don't want to have it expanded by the shell...? Update 2: Hm, actually it seems from your log output that the variable does get expanded, but later, so I guess inside iax_0taskchklog.pl?

      First of all Thank you very much for your reply

      The most frustrated thing that can happen to a developer did happen: I was trying to debug the error following your advise (checking the value of the signal to verify whether if the script completed, printing the content of the output @flush)... When the error disappeared. The machine is the same and I have only introduced debugging lines, so now I have the unsettling feeling that my code hides a spurious bug.

      But at least it is safer and it will be easier to find what is going on with the lines you suggested the next time it fails :)

      I also realised thanks to your observations that it is useless and even redundant to execute start before the script, since start is precisely the executable file that sets the value of $UNXEXSCRIPT, it's own path.

      The script iax_0tasksimp.pl is simply a launcher of script with a timeout and check of return code and output.

      The script iax_0taskchklog.pl is a launcher of script with a timeout check of return code, output and line in a log.

      These lauchers do verify the whole $??, and I did the timeout with a countdown child and interruption to the father, who kills the child performing the main task (but not the grandchildren, that is tricky for my level). Surely perl has a built-in library to do the same (launching a script with a timeout/kill and return code verification).

      The script that encapsulates them just launch these "launchers" sequentially. They are generic mini tools that I reuse for multiple purposes.

      Just in case you are curious, in this case I am using these little tools to redefine a Tomcat instance, for that I have a perl script that launchs sequentially:

      - 1) iax_0taskchklog.pl launching a certain script tbtom.sh stop_all /users/iax00/exploit/data/tom85/admin_portal.xml, with a timeout of 15 seconds, expecting it to return a code 0 and looking for the message "Command successful. Status: 0." to appear in its log. If it is successful, it goes on, if not, it aborts.

      - 2) iax_0taskchklog.pl launching tbtom.sh delete_all /users/iax00/exploit/data/tom85/admin_portal.xml, with a timeout of 15 seconds, expecting it to return a code 0 and looking for the message "Command successful. Status: 0." to appear in its log. If it is successful, it goes on, if not, it aborts.

      - 3) iax_0taskchklog.pl launching tbtom.sh define_all /users/iax00/exploit/data/tom85/admin_portal.xml, with a timeout of 15 seconds, expecting it to return a code 0 and looking for the message "Command successful. Status: 0." to appear in its log. If it is successful, it goes on, if not, it aborts.

      - 4) iax_0taskchklog.pl launching tbtom.sh start_all /users/iax00/exploit/data/tom85/admin_portal.xml, with a timeout of 15 seconds, expecting it to return a code 0 and looking for the message "Command successful. Status: 0." to appear in its log. If it is successful, it goes on, if not, it aborts.

      - Finally, iax_0tasksimp.pl launching a verifTomcatAdminPortal.sh, which returns 0 if there is one and only one instance tomcat called admin_portal running (I do this with a simple ps | grep ), and 5 if it fails. The problem was that there was no tomcat running, and the verification was still ok, manually, my iax_0tasksimp.pl launching verifTomcatAdminPortal.sh returned 5, but within the sequence of tasks, it said the return code was 0.

      The previous steps tbtom.sh give all an OK and they should not, they should return an error at some point, as the tomcat instance has not started, but I have checked that they indeed return 0, and its log has a happy final line "Command successful. Status: 0." at each execution. tbtom.sh is not my script tho, and I am not allowed to touch it, just to report its malfunction (the same way that I expect that my colleagues report my bugs if they use my launchers. And I don't mind at all if they touch them, although generally they do not have the time :)

        now I have the unsettling feeling that my code hides a spurious bug

        Well, that's a good argument for more defensive coding - you said that your scripts are mainly task launchers, so I'll repeat my suggestion to check all possible error conditions, or use a module to do it for you (e.g. IPC::System::Simple). One thing I'd also check is that the processes' STDOUT and STDERR is properly monitored - there are some cases where tools might exit with a code of zero, but have printed some serious warnings to STDERR. Of course it's also a good argument for writing tests for your code :-)

        For example, until I wrote my own module to simplify this, I'd sometimes write code something like this:

        use IPC::Run3 'run3'; sub myrun { my ($cmd) = @_; run3 $cmd, undef, \my $out, \my $err or die "run3 failed"; my $rv=$?; chomp($err); die "command '$$cmd[0]' wrote to STDERR: '$err'" if $err; die "command '$$cmd[0]' exit value indicates error: \$?=$rv" unless $rv==0; return $out; }
        Surely perl has a built-in library to do the same (launching a script with a timeout/kill and return code verification).

        For timeout support, you could take a look at IPC::Run.

        launching a verifTomcatAdminPortal.sh ... a simple ps | grep

        Note that unless you do set -e or equivalent in the shell script, you might be missing errors there, which goes against my aforementioned advice to code defensively. It's something you can do in Perl - for example, you could use IPC::System::Simple's capturex to run ps and use Perl's grep. And of course there are modules too, e.g. Proc::ProcessTable.