Errors from a string eval can be tricky to track down since perl
doesn’t tell you where the eval was. It treats each of the string evals as a separate, virtual file because it doesn’t remember where the string argument came from. Since perl
compiles that during the run phase (see Know the phases of a Perl program’s execution), the information the compiler dragged along for filenames and line numbers is so longer around.
In general, a string eval is a dangerous and tricky feature, and most good Perl programmers will tell you not to use it (see Know the two different forms of eval). That, in general, is good advice. If you can reasonably get the job done without the string eval, you’re probably better off. Sometimes, however, you need the string eval, and for the rest of this Item I’ll assume you have a good reason.
Consider this bit of code that issues a warning and has a fatal error that the eval catches:
use v5.14; use warnings; say 'Hello'; eval q( my $string; print $string; die 'Dying in eval'; ); print $@;
The output shows the warning and the error, and each has a line number. However, there’s no filename and the line number doesn’t correspond either to the actual line of the statement of the line of the eval:
Hello Use of uninitialized value $string in say at (eval 1) line 3. Dying in eval at (eval 1) line 5.
You can solve this with a preprocessor comment. Few people know that Perl looks for a couple of special pre-processor commands, but it’s right there in perlsyn. You can change what perl
thinks the line number and filename are with a line that starts with # line
. You follow that with the line number and the filename (optionally in double quotes):
use warnings; my $string; # no value there! print $string; # gets right line and file in warning # line 58976 "Buster.pm" print $string; # line 65783 "Mimi.pm" print $string; # line 137 print $string;
Now, the warnings make it look like there are at least two modules having problems even though there is a single file. Without a filename in the command, perl
uses the previous filename, as in the last line of this output:
Use of uninitialized value $string in print at line.pl line 5. Use of uninitialized value $string in print at Buster.pm line 58976. Use of uninitialized value $string in print at Mimi.pm line 65783. Use of uninitialized value $string in print at Mimi.pm line 137.
You should do this only when Perl expects a new statement. If you place one of those pre-processor commands in the middle of a statement, odd things can happen:
use warnings; my $string; # no value there! print $string; # gets right line and file in warning print # line 58976 "Buster.pm" $string; # line 65783 "Mimi.pm" print $string; # line 137 print $string;
The output shows that perl
changed the current filename to Buster.pm but did not change the line number. For that warning, the line number is the actual line number:
Use of uninitialized value $string in print at line.pl line 5. Use of uninitialized value $string in print at Buster.pm line 7. Use of uninitialized value $string in print at Mimi.pm line 65783. Use of uninitialized value $string in print at Mimi.pm line 137.
Once you know that, you can use this in your string eval, which perl
compiles just like any other Perl code, including this pre-processor command. You can use the special literals __FILE__
and __LINE__
(documented in perldata) to get the current values instead of hard-coding them. You can only use those as separate tokens, which makes their use a bit messy.
This code is the same as the first example in this Item, put with the addition of a pre-processor line as the first line in the eval string:
use v5.14; use warnings; say 'Hello'; eval '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) . q( my $string; print $string; die 'Dying in eval'; ); say $@;
Now the eval warnings and error messages show a filename and line number, and the line number corresponds to its actual location in the file:
Hello Use of uninitialized value $string in print at eval.pl line 7. Dying in eval at eval.pl line 9.
Note that changing the line number and filename also change __LINE__
and __FILE__
, meaning there’s no way to get back to the actual line number and filename. As such, you probably only want to use these to represent the actual values:
use v5.14; use warnings; # line 58976 "Buster.pm" say "line ", __LINE__, " file ", __FILE__
line 58976 file Buster.pm
Try this but make sure the expression for my_eval starts in the same line as my_eval: