Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Handling weird return values with or die

by mvaline (Friar)
on Jul 05, 2001 at 16:27 UTC ( [id://94071]=perlquestion: print w/replies, xml ) Need Help??

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

I've got a little backup script running on Windows NT in which I call the InfoZIP zip program using the following construction: system("zip -rq $target $project_root") or die "couldn't exec zip: $!"; The creation of the zip file occurs correctly, but I always get the die message. I ran the following code as a test and verified that the return value was zero:
$test = system("zip x:\\test.zip x:\\test.txt"); print $test;
I checked the Camel Book, and sure enough, the or die construction executes the die portion if the left side is false. I guess I could be losing my memory, but I thought that normally executing programs return 0 to signify a normal exit. I use the or die construction all the time. At any rate, should I go ahead with something like not system("zip -rq $target $project_root") or die "couldn't exec zip: $!"; or is there something I'm not seeing here.

Replies are listed 'Best First'.
Re: Handling weird return values with or die
by azatoth (Curate) on Jul 05, 2001 at 16:29 UTC
      Thanks; your solution worked great! I hadn't been familiar with $? until now.
Re: Handling weird return values with or die
by jeroenes (Priest) on Jul 05, 2001 at 16:31 UTC
    But with system that is different. See the doc. Check for a number of -1 to die on start failure:
    system("blah foo beer!") <> -1 or die "error: $?";

    To die on the exit call, maybe you'd better use some elaborate sceme:

    system( @args ) == 0 or syscall_error( $?, "system @args" ); sub syscall_error( my $error = shift; my $name = shift; die "$name failed: $!" if $error == -1; die "$name returned error: ", $error >> 8, ", signal: ", $error & 127, " and dumped_core: ", $error & 128; }
    The last sub pulled from the literal info in system.

    Hope this helps,

    Jeroen
    "We are not alone"(FZ)
    Updated

Re: Handling weird return values with or die
by japhy (Canon) on Jul 05, 2001 at 16:37 UTC
    The problem is not with or. The problem is with system(). Unlike functions like open(), unlink(), and chmod(), this does not return a true value on success -- rather, it turns 0 (the shell's version of true) on success, and non-zero on failure. This is documented.

    Many people write system(...) == 0 or die to get around this. You can use my Perl 6 module for "fixing" the senses of true and false, found at japhygesis. I'll probably put it on CPAN soon.

    japhy -- Perl and Regex Hacker
Re: Handling weird return values with or die
by Zaxo (Archbishop) on Jul 05, 2001 at 16:36 UTC

    Perl functions generally return true on success, but system() returns the system exit status, which is inverted. Your workaround is correct. Another clean but unintuitive way is:

    system("zip -rq $target $project_root") and die "couldn't exec zip: $!";

    After Compline,
    Zaxo

Re: Handling weird return values with or die
by mvaline (Friar) on Jul 05, 2001 at 16:48 UTC
    I obviously didn't read the entry for system in the Camel Book well enough, its code sample is:
    @args = ("command", "arg1", "arg2"); system(@args) == 0 or die "system @args failed: $?"

    Thanks, though... I've got it working now.

    Just out of curiosity, is there some interesting piece of lore that explains why perl functions and shell functions have opposing standards for return values? The return values for other perl functions such as open, unlink, etc. as cited above seem to be the more intuitive, but I guess confusion only comes from having multiple standards.

      Perl functions as it does to be a good fit to intuition and natural language.</p?

      A shell process's return value is the only way (aside from whatever it may have written to stderr) that status can be checked. Since there are many interesting ways for a process to fail, the ability to return different error codes is useful. Hence 0 == success, 0 != error_code.

      It takes some getting used to.

      After Compline,
      Zaxo

        I see; since boolean true would refer to any value above 0 and false only refers to to 0, and the fact that multiple error codes are much more useful than multiple success codes :-), zero has to be the success code. Thanks!
      It is not a matter of opposing standards. There are two different things. First, there are the function calls, whose behaviour is strictly defined (or else they would be unusuable). At the C level, failure is often indicated by a return value of -1, or NULL, and the reason of failure is passed in a global variable, errno. Not very intuitive (IMO), and Perl, being the thin layer it is above Unix/C, only makes it slightly more convenient. It returns 0 on failure. The reason of failure is still passed in a global variable, $! (which explains that variable).

      The return values of spawned process have nothing to do with the shell. It's the other way around. Processes typically return 0 indicating success, and anything else for failure. There's no global variable to set, all they can do is return a single integer. "0 on success" is a mere convention. It's just because this convention is followed so often, typical shell Boolean logic is "reversed"; 0 is true, other integers are false.

      -- Abigail

        Thank you; that was quite a good explanation. I had never considered the semantic relationship between errno and $!.

        I always find it helpful to learn context around the tools I use. I believe that if I can think a little more like the designer of the tool when I'm using it, I will be that much more effective.

      I'd say the truthness values come from C.

      As C language does not have a bool type, thruthness is expressed with anything different from 0, and 0 is false.

      The shell, (and certainly most programs in most OS's) return 0 when everything went OK, and an error code when not...

      Notice there'll be a my $rc = 0 is true;, which basically says the result of your sub is a 'zero' and that it's true (not an error, for example...). Only available in Perl6 properties (I wish it arrives soon :-)

      laters, david

      sub foo { return join '~', @_ }

Re: Handling weird return values with or die
by bikeNomad (Priest) on Jul 05, 2001 at 20:00 UTC
    Or, you could just use Archive::Zip and do it all in Perl:

    use Archive::Zip; use Archive::Zip::Tree; use File::Temp; use File::Basename; my $zip = Archive::Zip->new(); $zip->read($target) and die "can't read $target\n" if -f $target; $zip->addTree($project_root, $project_root); if (-f $target) # existing file? { my ($tmpfh, $tmpname) = File::Temp::tempfile( DIR => dirname($target +)); $zip->writeToFileHandle( $tmpfh ) and die "Can't write to $tmpname\n +"; $tmpfh->close(); unlink($target) or die "can't remove $target: $!\n"; rename($tmpname, $target) or die "can't rename $target: $!\n"; } else { $zip->writeToFileNamed($target) and die "can't write to $target\n" +; }

    Hmm... maybe I should add the write back to same file ability to Archive::Zip.

      Thanks! I only carry around the zip program for my scripts to use; now I don't have to.

      /me makes mental note to always check CPAN before he does anything. :-)

Re: Handling weird return values with or die
by Abigail (Deacon) on Jul 05, 2001 at 19:46 UTC
    As you are saying, normally executing programs return 0 to signify a normal exit. The Perl expression EXPR1 or EXPR2 evaluates EXPR2 only if EXPR1 is false. 0 is false.... So, what you see happening is exactly what should be happening.

    I guess you are using or die on system *calls* (like open or unlink), but system is not a system call.

    -- Abigail

Re: Handling weird return values with or die
by sierrathedog04 (Hermit) on Jul 06, 2001 at 00:10 UTC
    not system("zip -rq $target $project_root") or die "couldn't exec zip: $!"; reduces to die "couldn't exec zip: $!" if system ("zip -rq $target $project_root"); Might as well reduce the two keywords "not" and "or" to just one "if".
      Except that breaks the principle of "put the most important thing first." if(system("whatever")) { die "whatever else" } doesn't, however...

      =cut
      --Brent Dax

      @HPAJ=split("", "rekcaH lreP rentonA tsuJ"); print reverse @HPAJ; #sucky but who cares?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://94071]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (6)
As of 2024-04-18 01:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found