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

时间:2014-08-19 04:13:51

标签: perl eval

我需要eval Perl中的某些代码,有时可能会在其中包含exit()调用。一个非常简单的例子是:

use strict;
use warnings;


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

print "Still alive!\n";


sub some_function {
    print "Hello from some_function\n";
    exit;
}

我永远不会“活着!”因为exit()来电。

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

exit()如何阻止eval来电有效?

2 个答案:

答案 0 :(得分: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 $@:

但这会产生一个可能无意中发现的异常。所以我们改用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;

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

答案 1 :(得分:3)

exit并不意味着被困,所以eval不是解决方案。您可以将剩余的代码放在END块中运行:

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

sub some_function {
    print "Hello from some_function\n";
    exit;
}

但如果你绝对需要阻止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 }

如果您对更强大的解决方案感兴趣,可以使用CPAN中的Test :: Trap模块为您封装这一点丑陋。我个人会在本地修补exit ing some_function()以使用croak,如果它是一个模块,可能会在补丁中提交错误报告。

如果您只是eval用户输入,并且您不希望他们能够拨打exit,请确认该字符串不包含对{{1}的调用或者是一个间接退出的子程序,然后是exit它。就个人而言,如果用户输入要评估的任意代码,我会比evalunlink更害怕fork