Trap errors when eval string content to a hash p
I'm trying to transform a hash definition which is stored in a string to an actual hash. This works out great with the eval() function.
I want to however to have the possibility to trap errors when an faulty hash definition 开发者_运维百科is stored in the string.
Why can't I catch/trap the error which occurs in line 9?
#!/usr/bin/perl
use warnings;
use strict;
my $good_hash = "( 1 => 'one', 2 => 'two')";
my $bad_hash = "[ 1 => 'one', 2 => 'two')";
eval{my %string = eval($good_hash)} or &error;
eval{my %string = eval($bad_hash)} or &error;
sub error(){
print "error\n";
}
The eval operation can throw either errors or warnings.
The error messages from eval are stored in the $@ variable. If there was no error thrown , $@ will be an empty string.
However, warning messages are not stored in the $@ variable. You can process the warnings by using $SIG{__WARN__}
.
I think in your case, eval is throwing warnings. One way of handling it would be by doing something like this:
#!/usr/bin/perl
use warnings;
use strict;
BEGIN { $SIG{'__WARN__'} = sub { error ($_[0]); }}
my $good_hash = "( 1 => 'one', 2 => 'two')";
my $bad_hash = "[ 1 => 'one', 2 => 'two')";
eval{my %string = eval($good_hash)}; error($@) if ($@);
eval{my %string = eval($bad_hash)}; error($@) if ($@);
# sub error will be called both in case of errors and warning.
sub error
{
my ($msg ) = @_;
print "Error/ warning message - $msg\n";
}
This is simplistic code example and can be improved based on your requirement.
You can't catch the "error" because it's just a warning, not an error.
Do you have to use a Perl hash, or can you use json, xml, or even Storable to store the data?
An easier way to visualise this is to remember that eval executes an expression or code block. Giving it a complete expression makes things a little bit easier to understand:
#!/usr/bin/perl
use warnings;
use strict;
my $good_hash = "\%string = ( 1 => 'one', 2 => 'two')";
my $bad_hash = "\%string = [ 1 => 'one', 2 => 'two')";
my %string;
eval $good_hash;
if ($@) {
error("can't process good_hash: $@");
}
eval $bad_hash;
if ($@) {
error("can't process bad_hash: $@");
}
sub error {
my $msg = shift;
print "error: $msg\n";
}
Note the pre-declaration of %string, how the two strings contain complete perl expressions and how we look at $@ to see what the actual error was.
Your error sub should print "error $@"
because $@
(aka $EVAL_ERROR via English
) tells you what error made the eval
end. You can't just print out a string "error"
and expect Perl to know that you want to print an error.
I recommend that you use a regex to test the string before eval-ing it
something like:
if($hash =~ /^\((\s?[a-z0-9]+\s?=>\s?'[a-z0-9]*'\s?,?)*\)$/){
eval{my %string = eval($hash)};
} else {&error;}
This should also help to prevent invalid inputs from being executed which may cause bad things to happen on your system....
精彩评论