How to override exit() call in Perl eval block
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.
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(;$) {
CORE::exit( $_[0] // 0 ) if !$override_exit;
die "EXIT_OVERRIDE\n";
};
}
eval {
local $override_exit = 1;
some_function();
};
if ( !$@ ) {
say "Normal return";
} elsif ( $@ eq "EXIT_OVERRIDE\n" ) {
say "Exit called";
} else {
print "Exception: $@";
}
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(;$) {
CORE::exit( $_[0] // 0 ) if !$override_exit;
no warnings qw( exiting );
last EXIT_OVERRIDE;
};
}
my $exit_called = 1;
EXIT_OVERRIDE: {
local $override_exit = 1;
eval { some_function() };
$exit_called = 0;
}
if ( $exit_called ) {
say "Exit called";
} elsif ( $@ ) {
print "Exception: $@";
} else {
say "Normal return";
}
Note that eval BLOCK
is used to catch exceptions. eval EXPR
is used to compile code.