Приглашаем посетить
Аксаков К.С. (aksakov-k-s.lit-info.ru)

Section 16.1.  Trapping Errors with eval

Previous
Table of Contents
Next

16.1. Trapping Errors with eval

Sometimes, your ordinary, everyday code can cause a fatal error in your program. Each of these typical statements could crash a program:

    $barney = $fred / $dino;         # divide-by-zero error?

    print "match\n" if /^($wilma)/;  # illegal regular expression error?

    open CAVEMAN, $fred              # user-generated error from die?
      or die "Can't open file '$fred' for input: $!";

You could go to some trouble to catch some of these, but it's hard to get them all. (How could you check the string $wilma from that example to ensure it makes a valid regular expression?) Fortunately, Perl provides a way to catch fatal errors: wrap the code in an eval block:

    eval { $barney = $fred / $dino } ;

Even if $dino is zero, that line won't crash the program. The eval is an expression (not a control structure, like while or foreach), so the semicolon is required at the end of the block.

When a normally fatal error happens during the execution of an eval block, the block is done running, but the program doesn't crash. Right after an eval finishes, you'll be wanting to know if it exited normally or caught a fatal error for you. The answer is in the special $@ variable. If the eval caught a fatal error, $@ will hold what would have been the program's dying words, perhaps something like: Illegal division by zero at my_program line 12. If there was no error, $@ will be empty. That means that $@ is a useful Boolean (true/false) value (true if there was an error), so you'll sometimes see code like this after an eval block:

    print "An error occurred: $@" if $@;

The eval block is a true block, so it makes a new scope for lexical (my) variables. This piece of a program shows an eval block hard at work:

    foreach my $person (qw/ fred wilma betty barney dino pebbles /) {
      eval {
        open FILE, "<$person"
          or die "Can't open file '$person': $!";

        my($total, $count);

        while (<FILE>) {
          $total += $_;
          $count++;
        }

        my $average = $total/$count;
        print "Average for file $person was $average\n";

        &do_something($person, $average);
      };

      if ($@) {
        print "An error occurred ($@), continuing\n";
      }
    }

How many possible fatal errors are being trapped here? If there is an error in opening the file, that error is trapped. Calculating the average may divide by zero, so that error is trapped. Even the call to the mysteriously named &do_something subroutine is protected against fatal errors because an eval block traps any otherwise fatal errors that occur during the time it's active. (This feature is handy if you have to call a subroutine written by someone else, and you don't know if he has coded defensively enough to avoid crashing your program.)

If an error occurs during the processing of one of the files, we'll get an error message, but the program will go on to the next file without further complaint.

You can nest eval blocks inside other eval blocks. The inner one traps errors while it is running, keeping them from reaching the outer blocks. (After the inner eval finishes, you may wish to re-post any errors by using die, thereby letting the outer eval catch it.) An eval block traps any errors that occur during its execution, including errors that happen during subroutine calls (as in the earlier example).

The eval is an expression, which is why the trailing semicolon is needed after the closing curly brace. Since it's an expression, it has a return value. If there's no error, it's like a subroutine: The return value is the last expression evaluated, or it's returned early with an optional return keyword. Here's another way to do the math without having to worry about dividing by zero:

    my $barney = eval { $fred / $dino };

If the eval traps a fatal error, the return value is undef or an empty list, depending on the context. So, in the previous example, $barney is the correct result from dividing, or it's undef. We don't need to check $@ before we use it further although it's probably a good idea to check defined($barney) before we use it further.

There are four kinds of problems that eval can't trap. The first group is the serious errors that crash Perl, such as running out of memory or getting an untrapped signal. Since Perl isn't running, it can't trap these errors.[*] Syntax errors inside the eval block are caught at compile timethey're never returned in $@.

[*] Some of these errors are listed with an (X) code on the perldiag manpage, if you're curious.

The exit operator terminates the program immediately, even if it's called from a subroutine inside an eval block. This correctly implies that when writing a subroutine, you should use die rather than exit to signal when something goes wrong.

The fourth problem an eval block can't trap are warnings: either user-generated ones (from warn) or Perl's internally generated warnings (requested with the -w command-line option or the use warnings pragma). There's a separate mechanism from eval for trapping warnings. See the discussion of the _ _WARN_ _ pseudosignal in the Perl documentation for the details.

There's another form of eval that can be dangerous if it's mishandled. Sometimes, you'll meet people who say you shouldn't use eval in your code for security reasons. They're (mostly) right that eval should be used with great care, but they're talking about the other form of eval, sometimes called "eval of a string." If the keyword eval is followed directly by a block of code in curly braces, as we're doing here, there's no need to worrythat's the safe kind of eval.

    Previous
    Table of Contents
    Next