http://qs321.pair.com?node_id=486200

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

Using perl 5.8.0 on Solaris, I sometimes use system calls to manipulate a file or directory. This has worked well in the past, but this week a script failed trying to copy a file. The error message was "Illegal seek at ./script.pl line XXX", and the system call causing the error is here:
use strict; system( "cp /analysis/fasta1.fa /analysis2/fasta1.fa" ) or die print "Can't copy fasta file: $! \n";
A friend at work said that in his experience, the return code from "system" isn't reliable when used that way. He said I should capture the actual return code from the system call and evaluate it. If it's not zero, there's an error and to print $! at that point. I followed his recommendation and the problem went away. Here's the code I used.
use strict; &doSystemCommand( "cp /analysis/fasta1.fa /analysis2/fasta1.fa" ); sub doSystemCommand { my $systemCommand = $_[0]; print LOG "$0: Executing [$systemCommand] \n"; my $returnCode = system( $systemCommand ); if ( $returnCode != 0 ) { die "Failed executing [$systemCommand]\n"; } }
Could you tell me more about what is happening here and why this eliminated the errors? Also, could you offer improvements in this function for handling system calls?

Replies are listed 'Best First'.
Re: Best method to capture return code from system calls?
by dave_the_m (Monsignor) on Aug 24, 2005 at 14:52 UTC
    Note that system() returns a false value on success, so system(...) or ... is the wrong idiom. Your code was succeeding, then printing out a suprious error.

    perlfunc demonstrates the most comprehensive checking code:

    system(...); 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; }
    Note some further subtleties: since you are giving the command as a single string, it is executed by a shell, and you are actually checking the exit status of the shell rather than the command the shell is executing (although the shell normally returns the code of the last command it executed.)

    But as has been pointed out, use File::Copy.

    Dave.

Re: Best method to capture return code from system calls?
by davidrw (Prior) on Aug 24, 2005 at 14:49 UTC
    regarding the return code from system, here's a perldoc -f system snippet:
                   The return value is the exit status of the program
                   as returned by the "wait" call.  To get the actual
                   exit value divide by 256. 
        -- snip --
                   You can check all the failure possibilities by
                   inspecting "$?" like this:
    
                       $exit_value  = $? >> 8;
                       $signal_num  = $? & 127;
                       $dumped_core = $? & 128;
    
    See also the $? section in perldoc perlvar. In brief, it is The status returned by the last pipe close, backtick ("``") command, successful call to wait() or waitpid(), or from the system() operator.
Re: Best method to capture return code from system calls?
by sunadmn (Curate) on Aug 24, 2005 at 14:37 UTC
    In my experiance with running sytem calls on SUN I have always built my command like this:

    my $cpcmd = '/bin/cp'; system($cpcmd $curfile $newfile);
    But there is a better way of doing this and that would be using File::Copy take a look at that.
    SUNADMN
    USE PERL
Re: Best method to capture return code from system calls?
by pbeckingham (Parson) on Aug 24, 2005 at 14:38 UTC

    I use the following to return both the status and the captured STDOUT/STDERR. It is not as simple as just checking for non-zero return status - there are bits that must be ignored.

    sub executeCommand { my $command = join ' ', @_; reverse ($_ = qx{$command 2>&1}, $? >> 8); }
    Would someone please critique this approach?



    pbeckingham - typist, perishable vertebrate.
Re: Best method to capture return code from system calls?
by Anonymous Monk on Mar 30, 2011 at 07:58 UTC
    The return code from "system" is not unreliable, you're just interpreting it backwards. It's a really easy mistake to make - the shell interprets 0 as success and nonzero as failure. So a zero return code from a system call means it succeeded. The actual return code, if nonzero, is returned in the high-order byte of the return code, so if the shell returns 1 you'll get 256. That is usually not what you care about, you just want to know did it succeed or fail. So - change "or" to "and" and you're all set. It is clearer to say: if(system(...) != 0) { die(" ... "); }