On Thu, Jun 12, 2003 at 09:07:48PM +0100, Nick Drage wrote:
> I was given this answer by David van der Geer, who used:
> 
> $exit_value = $? >> 8;
> 
> I'm not sure what " >> 8 " means though, anyone?

Short answer: It's the right-shift operator.  It takes an
integer and shifts all the bits to the right.

Longer answer: backticks and system() both launch a subprocess
and wait for it to terminate.  The system calls to wait for
a child process to terminate are called wait() and waitpid(),
just like in Perl, and they return several pieces of information
squeezed into a 16-bit integer:

     7  6  5  4  3  2  1  0  7  6  5  4  3  2  1  0
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  [ $? ]
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    \_____________________/ \/ \__________________/
          exit code        core    signal number

Before backticks or system() returns, Perl will call waitpid() 
for you and put the 16-bit status into $?.  Then you can use $?
to find out whether the child succeeded, and if necessary, find
its exit code.  

Or the signal that killed it, or whether it dumped core.  But 
since all of this is in one integer, you need to use the bitwise 
operators '>>' and '&' to extract each piece.

To get the exit code, you use right-shift.

    $exit = $? >> 8;

     7  6  5  4  3  2  1  0  7  6  5  4  3  2  1  0
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | [ $exit ]
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
           [ zero ]         \_____________________/
                                  exit code  

And if the child failed and did "exit 1", we'll get that
number in $exit.  (I'll give a real example later.)

It's worth emphasizing the fact that you only have eight bits to 
work with here.  If the child did "exit 256", it would look to the 
parent like "exit 0". (!)

Now, if the process didn't exit normally -- i.e. if it aborted
or was killed -- then you'd mask out everything but the low 7 
bits to get the signal number.

    $sig = $? & 127;

     7  6  5  4  3  2  1  0  7  6  5  4  3  2  1  0
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  [ $? ]
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   | 0| 0| 0| 0| 0| 0| 0| 0| 0| 1| 1| 1| 1| 1| 1| 1|  [ 127 ]
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

                              +--+--+--+--+--+--+--+
           [ zero ]           |  |  |  |  |  |  |  |  [ $sig ]
                              +--+--+--+--+--+--+--+
                               \__________________/
                                   signal number

And some of the signals can produce a core dump, so you mask 
out everything but the eighth bit to see whether the subprocess 
dumped core.

    $core = $? & 128;

     7  6  5  4  3  2  1  0  7  6  5  4  3  2  1  0
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  [ $? ]
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   | 0| 0| 0| 0| 0| 0| 0| 0| 1| 0| 0| 0| 0| 0| 0| 0|  [ 128 ]
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

                           +--+
           [ zero ]        |  | --> core dumped       [ $core ]
                           +--+

And that's it.  To show how it fits together, here (finally :-) 
are some real examples:

(1) Perl can't run the external program.

    In this case, perl tries to fork and exec "/no/such/file".

    If either the fork or exec fails, perl will set $? to -1
    and put the error in $!.

    noent.pl:
    ---------------------------------------
    #!/usr/bin/perl
    system "/no/such/file";

    print  "\$? is $?\n";  # exit status
    print  "\$! is $!\n";  # errno
    ---------------------------------------

    output:
    ---------------------------------------
    % noent.pl
    $? is -1
    $! is No such file or directory
    ---------------------------------------

    This is the only time $! is meaningful after system() or 
    backticks.  If $? is greater than -1, you can ignore $!.

    For the rest of the examples we'll use this script instead,
    since it displays $? and the bitwise >> and &.

    report.pl:
    ---------------------------------------
    #!/usr/bin/perl

    my $cmd = shift 
      or die "usage: $0 'shell-command'\n";

    system $cmd;

    print  "\$? is $?\n";
    print  "\$! is $!\n";

    printf "[ status ] %016b [%d]\n", ($?) x 2;
    printf "[ exit   ] %016b [%d]\n", ($? >> 8 ) x 2; 
    printf "[ core   ] %016b [%d]\n", ($? & 128) x 2;
    printf "[ signal ] %016b [%d]\n", ($? & 127) x 2;
    ---------------------------------------

    The %016b will print the binary-string representation of
    the numbers, so you can see what those ASCII diagrams
    were all about.


(2) Shell can't run the external program.

    In this one, Perl sees a shell metacharacter (the '*')
    and so it doesn't try to exec "/no/such/file".

    Instead it execs the shell, somewhat like this:

        exec 'sh', '-c', '/no/such/file/ *';

    And the output is quite different:

    ---------------------------------------
    % report.pl '/no/such/file *'
    sh: /no/such/file: No such file or directory
    $? is 32512
    $! is 
    [ status ] 0111111100000000 [32512]
    [ exit   ] 0000000001111111 [127]
    [ core   ] 0000000000000000 [0]
    [ signal ] 0000000000000000 [0]
    ---------------------------------------

    Here the *shell* printed "No such file or directory"
    and the Perl program doesn't know exactly what happened.

    But we can see that the subprocess did the equivalent of
    "exit 127" and die or log the error, etc.


(3) The external program fails.

    In this one Perl exececutes /bin/ls successfully, but
    ls itself fails.

    ---------------------------------------
    % report.pl '/bin/ls /no/such/file'
    /bin/ls: /no/such/file: No such file or directory
    $? is 256
    $! is 
    [ status ] 0000000100000000 [256]
    [ exit   ] 0000000000000001 [1]
    [ core   ] 0000000000000000 [0]
    [ signal ] 0000000000000000 [0]
    ---------------------------------------


(4) The external program is killed.

    Almost done... we'll kill this one from the terminal:

    ---------------------------------------
    % report.pl 'sleep 2'
    ^\
    $? is 131
    $! is 
    [ status ] 0000000010000011 [131]
    [ exit   ] 0000000000000000 [0]
    [ core   ] 0000000010000000 [128]
    [ signal ] 0000000000000011 [3]
    ---------------------------------------

    And the exit code is zero.  (!)

    But $? is not zero and you can see the signal number 
    and the flag for the core dump.

AND SO that's that.  It's probably too much information,
really, since system() or backticks are destined for a 
life of quick'n'dirty scripts and lax error-handling...

:-)

-- 
Steve

-- 
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to