如何编写不在 eval 块中触发的 SIG{__DIE__} 处理程序?
How can I write a SIG{__DIE__} handler that does not trigger in eval blocks?
根据 perldoc -f die
,哪些文档 $SIG{__DIE__}
Although this feature was to be run only right before your program was to exit, this is not currently so: the $SIG{__DIE__}
hook is currently called even inside evaled blocks/strings! If one wants the hook to do nothing in such situations, put die @_ if $^S;
as the first line of the handler (see $^S
in perlvar). Because this promotes strange action at a distance, this counterintuitive behavior may be fixed in a future release.
所以让我们采用一个基本的信号处理程序,它将用 eval { die 42 }
、
触发
package Stupid::Insanity {
BEGIN { $SIG{__DIE__} = sub { print STDERR "ERROR"; exit; }; }
}
我们通过
确保安全
package Stupid::Insanity {
BEGIN { $SIG{__DIE__} = sub { return if $^S; print STDERR "ERROR"; exit; }; }
}
现在 不会 触发 eval { die 42 }
,但当相同代码位于 BEGIN {}
块中时它会触发
BEGIN { eval { die 42 } }
这可能看起来晦涩难懂,但它是真实世界,正如您所看到的 , or in my case specifically here Net::DNS::Parameters
。你可能认为你也可以抓住编译阶段,像这样,
BEGIN {
$SIG{__DIE__} = sub {
return if ${^GLOBAL_PHASE} eq 'START' || $^S;
print STDERR "ERROR";
exit;
};
}
这将适用于上述情况,但遗憾的是它将不适用于对其中包含 BEGIN 语句的文档的要求,
eval "BEGIN { eval { die 42 } }";
有没有办法解决这个问题并编写一个不干扰 eval
的 $SIG{__DIE__}
处理程序?
勾选caller(1)
[caller(1)]->[3] eq '(eval)'
的兔子洞再往下一点
return if [caller(1)]->[3] eq '(eval)' || ${^GLOBAL_PHASE} eq 'START' || $^S;
抓取整个调用堆栈
您可以抓取整个堆栈并确保您没有深入评估,
for ( my $i = 0; my @frame = caller($i); $i++ ) {
return if $frame[3] eq '(eval)'
}
是的,这完全是精神错乱。感谢 mst on irc.freenode.net/#perl
的指点。
根据 perldoc -f die
,哪些文档 $SIG{__DIE__}
Although this feature was to be run only right before your program was to exit, this is not currently so: the
$SIG{__DIE__}
hook is currently called even inside evaled blocks/strings! If one wants the hook to do nothing in such situations, putdie @_ if $^S;
as the first line of the handler (see$^S
in perlvar). Because this promotes strange action at a distance, this counterintuitive behavior may be fixed in a future release.
所以让我们采用一个基本的信号处理程序,它将用 eval { die 42 }
、
package Stupid::Insanity {
BEGIN { $SIG{__DIE__} = sub { print STDERR "ERROR"; exit; }; }
}
我们通过
确保安全package Stupid::Insanity {
BEGIN { $SIG{__DIE__} = sub { return if $^S; print STDERR "ERROR"; exit; }; }
}
现在 不会 触发 eval { die 42 }
,但当相同代码位于 BEGIN {}
块中时它会触发
BEGIN { eval { die 42 } }
这可能看起来晦涩难懂,但它是真实世界,正如您所看到的 Net::DNS::Parameters
。你可能认为你也可以抓住编译阶段,像这样,
BEGIN {
$SIG{__DIE__} = sub {
return if ${^GLOBAL_PHASE} eq 'START' || $^S;
print STDERR "ERROR";
exit;
};
}
这将适用于上述情况,但遗憾的是它将不适用于对其中包含 BEGIN 语句的文档的要求,
eval "BEGIN { eval { die 42 } }";
有没有办法解决这个问题并编写一个不干扰 eval
的 $SIG{__DIE__}
处理程序?
勾选caller(1)
[caller(1)]->[3] eq '(eval)'
return if [caller(1)]->[3] eq '(eval)' || ${^GLOBAL_PHASE} eq 'START' || $^S;
抓取整个调用堆栈
您可以抓取整个堆栈并确保您没有深入评估,
for ( my $i = 0; my @frame = caller($i); $i++ ) {
return if $frame[3] eq '(eval)'
}
是的,这完全是精神错乱。感谢 mst on irc.freenode.net/#perl
的指点。