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

Typically you would do:

$ftp->put($my_file) or die "Couldn't upload file";

But I want to do something else instead of just dying in this script so I tried:

$ftp->put($my_file) or { log("Couldn't upload $my_file"); return(-1); } log("$my_file uploaded");

But Perl complains of compilation errors saying: syntax error at toto.pl line nnn, near "log" which is the second log in my code fragment. Any suggestions greatly appreciated.

Replies are listed 'Best First'.
Re: How do I use a block as an ‘or’ clause instead of a simple die?
by Anonymous Monk on Apr 18, 2009 at 13:48 UTC
Re: How do I use a block as an ‘or’ clause instead of a simple die?
by linuxer (Curate) on Apr 18, 2009 at 14:27 UTC
    if ( !$ftp->put($myfile) ) { # do # your excessive exercise # her }
      The limit of if is encountered if you declare a variable.
      if (!open(my $fh, '<', $qfn)) { ... } print $fh $s; # XXX
      if (my ($y) = f($x)) { # Returns () on error. ... } print defined($y) ? $y : '[undef]'; # XXX

      Solutions:

      • Declare the var earlier:

        my $fh; if (!open($fh, '<', $qfn)) { ... }
        my $y; if (($y) = f($x)) { # Returns () on error. ... }
      • Save the result:

        my $success = open(my $fh, '<', $qfn); if (!$success) { ... }
        my $success = my ($y) = f($x); # Returns () on error. if (!$success) { ... }
      • Use do:

        open(my $fh, '<', $qfn) or do { ... };
        my ($y) = f($x) # Returns () on error. or do { ... };
Re: How do I use a block as an ‘or’ clause instead of a simple die?
by ELISHEVA (Prior) on Apr 18, 2009 at 18:21 UTC

    To process exceptions rather than automatically dying:

    eval { # my code here # for example, a call to whatever subroutine is calling # $ftp->put($my_file) or die return 1; } or do { my $err=$@; # my exception handling code here, uses $err, not $@ }

    And some explanatory notes:

    • eval {...} is an eval block. It prevents exceptions from killing your script.
    • return 1; or do {...} will be triggered if (a) something within eval {...} dies or (b) nothing dies, but the final statement evaluates to any "false" value: 0, undef, or the empty string. Since we only want to get into the or do {...} when something dies, we have to make sure that the last line is always true. Returning 1 does that. Note: returning within an eval block only exits the eval block, not any containing sub.
    • do {....} Anything more than a single statement after "or" needs to be enclosed in a do block.
    • my $err=$@ $@ holds the exception describing why we died. However, it is rather volatile and gets reassigned the next time we do something wrong. So we save it to another variable before we do anything that could change its value.

    Best, beth

      we have to make sure that the last line is always true. Returning 1 does that
      Your idiom would jump out of any subroutine you are in at the moment. Just 1; would be sufficient to have a value of 1 'returned' and avoid the or branch.

      Forget the above, it is wrong. As per the docs: return EXPR: Returns from a subroutine, eval, or do FILE with the value given in EXPR

      CountZero

      A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: How do I use a block as an ‘or’ clause instead of a simple die?
by Marshall (Canon) on Apr 18, 2009 at 18:32 UTC
    Problem #1: You have an "or" statement, not a subroutine block therefore a return(-1) is nonsense...return -1 to where and to whom?

    I snipped out some code from one of my LWP programs. This will show you how to do a retry. In my application, a retry is required about 1/2,500 or so requests. Ok let's say that just one of 1,000 attempts fail...that's still a small number but if I am gonna run 10,000 requests, that is gonna happen on most of the runs and in this case a retry almost always works, so I don't want to "die". First some code, then some discussion:

    #!/usr/bin/perl -w use strict; use constant MAX_RETRY => 2; my $ua = LWP::UserAgent->new; my $raw_html; my $someParam; RETRY: while (my $n_attempt=0, $someParam=<IN>) { #...some clean up of $someParam my $req = POST 'http://www.someURL/db/', [ action => '/db/', dbparam => "$someParam", type => 'submit', value => 'Search', ]; my $res = $ua->request($req); unless ($res->is_success) ## or perhaps: if ( !$res->is_success ) { $n_attempt++; print STDERR "$someParam ERROR: Try# $n_attempt" . $res->status_ +line . "\n"; sleep(1); redo RETRY if $n_attempt <= MAX_RETRY; print "$someParam,ERROR: Try# $n_attempt" . $res->status_ +line . "\n"; next; #Maybe you something different here???? } $raw_html = $res->as_string; #...do some normal processing... #then loops to next value of $someParam... }
    First set your loop up for the "good machine case" and get that working. Then think about the 1/1000 case that doesn't work. If the loop is gonna fail say 1/10th the time, then I would put the fail conditions in the loop iteration decision making. But if we are talking about a rare occurrence, I would put the processing of that in the "gut's"..not in the major loop condition! In your case ftp is gonna work! Well almost all of the time, given that you are authenticated, etc. But it can fail like 1/5000 times.

    Perl has a cool thing called "redo". Read about it in the manual, but basically this causes the loop to go again without evaluating the, in this case while() statement again. Here I used a labeled redo, called RETRY although here this RETRY label is not strictly necessary. I just thought it "looked better" and was easier to understand.

    Of course "next" operates just like what you would think..it loops back to and evaluates the loop condition again ("hey, I'm done with this one, I want a another one").

    Anyway when considering things to do other than "give up" and die, think about what you would do...is a retry likely to succeed? If something really "blows up" are you willing to deal with the aftermath? If some guy comes along and cuts my internet cable, this program will spew 30-100K lines of BS into a log file. I thought about that and I'm ok with it.

    In general your options are limited when "it didn't work". (a) try again, (b)skip this request and go the next request, (c) completely give up (die).

    I hope I've helped you with some ideas.

      Problem #1: You have an "or" statement, not a subroutine block therefore a return(-1) is nonsense...return -1 to where and to whom?

      No, there's no reason to believe there's a problem with return. The snippet comes from a sub, that's all.

      What I find odd is returning a true value on error. But it's not unheard of in specific circumstances (e.g. getc, index).

        I had to hit CTL-+ a couple of times to get fonts large enough that I could see the details. I see your point, but even so, this -1 return value is very strange...there can be complications with signed vs unsigned, etc.

        Returning a non-zero value for an error is actually the "normal" way to do things. If I can only transmit one single number to you and that number has to combine the error code and the success status into that single number, then "success" has to mean Zero. Zero means I am completely happy. Something non-Zero is an error code. What's weird here is the -1.

        This all has to do with ancient stuff. A lot of these machines only had two registers, A and B, each 16 bits. In ASM, by convention simple status would get passed back to caller via A register. PDP-11, SEL-810, NOVA-800,etc. Now if all the bits were "on" FFFF, then is that -1 or 65535?

        Even the most wimpy machines will have a JNZ (jump non zero) or JZ (jump zero) instruction - it can be called something else, but there will be something like that. To detect -1 would have taken 2 instructions: one's complement(XOR), then JNZ or JZ. On a modern processor, the math unit is FAR larger than the CPU. All this happens at mind boggling rates.

        Anyway, yes there is a false dichotomy concerning "true/false" values. All I can say is that it is what it is.