How to override exit() call in Perl eval block

假装没事ソ 提交于 2021-02-16 13:02:09

问题


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 evaled?


回答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 exiting some_function() to use croak instead, and maybe file a bug report with the patch if it's a module.

If you're just evaling 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!