我需要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编辑时生效?
您可以覆盖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用于编译代码.
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。