如何在Perl eval块中覆盖exit()调用

Fra*_*ozo 3 perl eval

我需要evalPerl中的一些代码,有时可能包含一个exit()调用.一个非常简单的例子是:

use strict;
use warnings;


eval "some_function()";
die $@ if $@;

print "Still alive!\n";


sub some_function {
    print "Hello from some_function\n";
    exit;
}
Run Code Online (Sandbox Code Playgroud)

我永远不会"活着!" 因为这个exit()电话.

我尝试设置一些键%SIG(QUIT,STOP,TERM,BREAK等),但这不起作用.我也试图重新定义CORE::exit,但没有成功.

如何防止exit()呼叫在被eval编辑时生效?

ike*_*ami 8

您可以覆盖exit,但必须在编译时执行此操作.因此,使用一个标志来表示覆盖是否有效.

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();
 };

 die "Exit was called\n" if $@ eq "EXIT_OVERRIDE\n";
 die $@ if $@:
Run Code Online (Sandbox Code Playgroud)

但这会产生一个可能无意中发现的异常.所以让我们last改用.

our $override_exit = 0;
BEGIN { 
    *CORE::GLOBAL::exit = sub (;$) {
        no warnings '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 $@;
 }
 die "Exit was called\n" if $exit_was_called;
Run Code Online (Sandbox Code Playgroud)

注意,eval BLOCK用于捕获异常.eval EXPR用于编译代码.


Sla*_*ade 6

exit不是为了被困,所以eval不是这里的解决方案。你可以把你需要运行的剩余代码放在一个END块中:

some_function();
END { print "Still alive! For now...\n"; }

sub some_function {
    print "Hello from some_function\n";
    exit;
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您绝对需要防止exit杀死脚本,则必须exit()在编译时重新定义:

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 }
Run Code Online (Sandbox Code Playgroud)

如果您对更强大的解决方案感兴趣,可以使用 CPAN 中的 Test::Trap 模块为您封装这种丑陋之处。就我个人而言,我会在本地修补exitingsome_function()以供使用croak,并且如果它是一个模块,则可能会随修补程序一起提交错误报告。

如果您只是eval输入用户输入并且不希望他们能够调用exit,请验证该字符串不包含对exit或间接退出的子例程的调用,然后eval它。就我个人而言unlink,如果用户输入要评估的任意代码,我会更害怕,而fork不是exit