问题
I need to eval
some code in Perl that might some times contain an exit()
call in it. A very simplified example of this would be:
use strict;
use warnings;
eval "some_function()";
die $@ if $@;
print "Still alive!\n";
sub some_function {
print "Hello from some_function\n";
exit;
}
I never get to "Still alive!" because of the exit()
call.
I tried setting some keys in %SIG
(QUIT, STOP, TERM, BREAK, etc) but that didn't work. I also attempted to redefine CORE::exit
with no success.
How can I prevent an exit()
call from being effective when being eval
ed?
回答1:
You can override exit
, but you must do so at compile-time. So use a flag to signal whether the override is active or not.
our $override_exit = 0;
BEGIN {
*CORE::GLOBAL::exit = sub(;$) {
die "EXIT_OVERRIDE\n" if $override_exit;
CORE::exit($_[0] // 0);
};
}
eval {
local $override_exit = 1;
some_function();
};
my $exit_was_called = $@ eq "EXIT_OVERRIDE\n";
die $@ if $@ && !$exit_was_called;
warn("Exit was called\n") if $exit_was_called;
But that creates an exception that might be caught unintentionally. So let's use last
instead.
our $override_exit = 0;
BEGIN {
*CORE::GLOBAL::exit = sub(;$) {
no warnings qw( exiting );
last EXIT_OVERRIDE if $override_exit;
CORE::exit($_[0] // 0);
};
}
my $exit_was_called = 1;
EXIT_OVERRIDE: {
local $override_exit = 1;
eval { some_function() };
$exit_was_called = 0;
die $@ if $@;
}
warn("Exit was called\n") if $exit_was_called;
Note that eval BLOCK
is used to catch exception. eval EXPR
is used to compile code.
回答2:
exit
isn't meant to be trapped, so eval
isn't the solution here. You could put the remaining code you need to run in an END
block:
some_function();
END { print "Still alive! For now...\n"; }
sub some_function {
print "Hello from some_function\n";
exit;
}
But if you absolutely, positively need to prevent exit
from killing the script, you'll have to redefine exit()
at compile time:
BEGIN { *CORE::GLOBAL::exit = sub (;$) { } } # Make exit() do nothing
some_function();
print "Still alive!\n"; # Gets printed
*CORE::GLOBAL::exit = *CORE::exit; # Restore exit()
exit;
print "I'm dead here.\n"; # Doesn't get printed
sub some_function { exit }
The Test::Trap module from the CPAN can be used to encapsulate this bit of ugliness for you, if you're interested in a more robust solution. Personally I would locally patch the exit
ing some_function()
to use croak
instead, and maybe file a bug report with the patch if it's a module.
If you're just eval
ing user input and you don't want them to be able to call exit
, verify that the string contains no calls to exit
or to a subroutine that would indirectly exit, then eval
it. Personally I'd be more afraid of unlink
and fork
than exit
if the user is inputting arbitrary code to be evaluated.
来源:https://stackoverflow.com/questions/25375579/how-to-override-exit-call-in-perl-eval-block