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

In perldoc -f system we find the following example:

system(....); if ($? == -1) { print "failed to execute: $!\n"; }
I tried this out on various platforms using the following program:

use strict; use warnings; system('no-such-program'); print $?
no-such-program doesn't exist, of course. Since it is the only argument to system, and there are no shell-metacharacters, Perl should (again according to the docs) bypass the shell and directly try to call the program. I received the following values for $?: Plenty of differences, but never got a -1...
-- 
Ronald Fischer <ynnor@mm.st>

Replies are listed 'Best First'.
Re: system and $?
by Limbic~Region (Chancellor) on Aug 26, 2009 at 12:27 UTC
    rovf,
    The most interesting thing to me isn't that you didn't get a -1 on any of those platforms but that Solaris gave you a 0. That means the program executed successfully - which you claim is impossible. According to the same docs, there are two reasons you might get a -1.

    Return value of -1 indicates a failure to start the program or an error of the wait(2) system call (inspect $! for the reason).

    You haven't mentioned what $! says but I am at a loss to explain your observed behavior. I typically need to bitshift $? to get the true exit code ($? >> 8) but that obviously does nothing for you here.

    Cheers - L~R

      You haven't mentioned what $! say

      In this posting, I was mainly interested why system() does not adhere to the docs in that it does not return -1 on some platforms even the execution failed.

      But - and maybe I should have mentioned this in my original post - the interest in this issue arose from a discussion with a co-worker about the proper usage of $!. My co-worker argued that it is never necessary to explicitly set $!=0 in a Perl program. His argument sounded convincing: $! (which is just errno) is only set if a low-level system call encounters a problem; otherwise, the value is not changed. Therefore, his argument goes, we first need to check if a function was successful, and only if it is not, we investigate $!. Example:

      if(open(FOO,'<',$foo)) { ... } else { print "open $foo: $!\n"; }
      This sounded convincing, but for the safe side, I tried to find a counter example, and came up with the example of system('myprog.exe'), where I can't, without prior setting of $!=0, reliably distinguish, whether or not myprog.exe has been executed with an exit code of 1, or could not be executed because it does not exist:

      If myprog.exe can not be run, system(...) returns on my platform 256 (which is exit code 1, no child-error), and, as you point out, sets $! to some error code. But if myprog.exe can be run, and exits with code 1, I also get 256 as return code, and $! has an undefined value (it is set to whatever it had been set before the call to system). Only by explicitly setting it to 0 prior to running system(), is it that I can distinguish between those cases.

      -- 
      Ronald Fischer <ynnor@mm.st>
        rovf,
        I have never set $! in a program to set the exit status - I have always used exit for that.
        #!/usr/bin/perl # ... rest of program exit(0); # assuming everything went according to plan
        Does that not work on windows?

        Cheers - L~R

        If myprog.exe can not be run, system(...) returns on my platform 256 (which is exit code 1, no child-error), and, as you point out, sets $! to some error code.

        You have a broken Perl if that's true.

        If myprog can not be run, system sets $? to -1 and sets the error in $!.

        If a shell command cannot be run, system sets $? to something other than -1 (since the shell was successfully executed) and $! contains no meaningful information. The shell cannot possibly change the parent's errno.

Re: system and $?
by Marshall (Canon) on Aug 26, 2009 at 12:17 UTC
    If you take another look at that page for ($?, $CHILD_ERROR), you will see that there are 2 other "if" clauses that follow the test for -1! This is a 16 bit value, not just the simple exit code. exit value of subprocess is high byte and low byte reports a signal. See page 661 of Programming Perl by Larry Wall. And oh, BTW decimal 256 is -1 in 8 bit lingo. Anyway this value is expected to be OS dependent.

    Update: I tried this on my WinXP system and it doesn't even get as far as the print. I get text error message: 'no-such-program' is not recognized as an internal or external command, operable program or batch file. This is what the shell would give out.

      you will see that there are 2 other "if" clauses that follow the test for -1!
      Exactly. But in the other cases, the high byte is supposed to contain the exit code of the invoked program, and the low byte the child error. As you can see from my posting, only Cygwin reports a child error, while on the other examples, the child error is 0 too.

      I think, the anonymous posting at Re: system and $? gives a clue of what is happening for the ActiveState/Windows case: Contrary to the documentation, the command shell *is* invoked, and it seems that CMD.EXE on Windows 2000 reports a non-existing program just by setting ERRORLEVEL to 1.

      As for Solaris, I found that Perl 5.8.8 does it correctly, so returning zero is a bug in 5.8.7.

      -- 
      Ronald Fischer <ynnor@mm.st>
Re: system and $?
by alexm (Chaplain) on Aug 26, 2009 at 12:11 UTC

    I'm getting -1 on all platforms and perl versions I tried:

    perl -le 'system "no-such-program"; print "$? $^O $]"'

    Results:

    • -1 hpux 5.006001
    • -1 solaris 5.008008
    • -1 dec_osf 5.006001
    • -1 linux 5.006001
    • -1 linux 5.008008
    • -1 linux 5.010000

Re: system and $?
by Anonymous Monk on Aug 26, 2009 at 12:12 UTC
    I know the documentation says the shell is not supposed to invoked, but on win32 it is being invoked
    C:\Temp>perl -le"system qw[dir]" Volume in drive C has no label. Volume Serial Number is FDB3-0FE3 Directory of C:\Temp 08/26/2009 05:05 AM <DIR> . 08/26/2009 05:05 AM <DIR> .. 0 File(s) 0 bytes 2 Dir(s) 2,157,553,920 bytes free File Not Found
    my @nosuch = qw[ nosuchfile ]; system {$nosuch[0]} @nosuch; printf qq{\$?(%d)=(%s)\n\$!(%d)=(%s)\n\n}, $?,$?,$!,$!; system @nosuch; printf qq{\$?(%d)=(%s)\n\$!(%d)=(%s)\n\n}, $?,$?,$!,$!; __END__ $?(65280)=(65280) $!(2)=(No such file or directory) 'nosuchfile' is not recognized as an internal or external command, operable program or batch file. $?(256)=(256) $!(2)=(No such file or directory)
Re: system and $?
by ikegami (Patriarch) on Aug 26, 2009 at 13:54 UTC

    If $? isn't -1, it means the shell was required and it was successfully executed. If the shell failed to execute the command, it will communicate this via $?. It chooses what value to return.

    If you want more precise diagnostics, don't invoke the shell.

      If you want more precise diagnostics, don't invoke the shell.

      Actually, in my own programs I already use often IPC::Run::run, and I think I will convert the remaining system() calls to the use of either IPC::Run or IPC::System::Simple, but we are here dealing with programmers from many different departments, and it would be a bit restrictive to have everyone drop system(), so I was interested in alternatives. But thanks a lot for the information.

      -- 
      Ronald Fischer <ynnor@mm.st>
Re: system and $?
by Sewi (Friar) on Aug 26, 2009 at 12:17 UTC
    To get the return value of the program, you need to check $? & 127:
    system "true"; echo $? & 127;
    If you need the "result" of the system command, get the return value:
    print system "does-not-exist"; print system "true"; print system "false";
    The results are -1, 0 and 256. Not really good, but here you get -1 in case of any kind of execution error.
    I agree that this behaviour isn't good at all and the documentation doesn't seem to match the reality. Sorry for this.
      you get -1 in case of any kind of execution error.

      Not on Windows 2000, as you can see from my example. I don't know about XP or Vista. Maybe someone could try this out?

      BTW, to get the return code on Windows 2000 (ActiveState Perl again - I don't know about Strawberry), you need to do a bit more logic than you posted, because you can't distinguish between a program execution error, and a program which runs fine but returns exit code 1. Here is what you have to do:

      $!=0; system('your_program_goes_here'); my $child_error=$? & 0xff; my $maybe_exit_code=$? >> 8; if($child_error || ($maybe_exit_code && $!)) { # there was an error in program execution }
      Also note that even if $?==0, it does not say that your program terminated with exit code 0. You get only the low 8 bit of the "real" Windows exit code, so if your program exited with a code which is a whole multiple of 256, you wouldn't notice it.
      -- 
      Ronald Fischer <ynnor@mm.st>