使用Perl的eval时常见的陷阱有哪些?

时间:2011-09-29 13:58:13

标签: perl exception eval

与Perl的eval相关的常见缺陷是什么,这可能会让您选择使用Try::Tiny等模块?

4 个答案:

答案 0 :(得分:25)

Perl的eval有两种形式,字符串eval和block eval。 String eval调用编译器来执行源代码。块eval将已编译的代码包含在一个包装器中,该包装器将捕获die异常。 (字符串eval也会捕获die异常,以及任何编译错误。)

Try :: Tiny仅适用于eval的块形式,但以下内容适用于两种形式。

每次拨打eval时,都会更改$@的值。如果eval成功或者eval捕获到错误,它将为''

这意味着每次调用eval时,都会清除以前的错误消息。 Try::Tiny为您本地化$@变量,以便成功的eval不会清除先前失败的eval的消息。

另一个缺陷来自于使用$@作为检查以确定eval是否成功。常见的模式是:

eval {...};
if ($@) {
   # deal with error here
}

这取决于两个假设,首先是任何错误消息$@可以包含的是一个真值(通常为真),并且eval块和if语句之间没有代码。

当然后者当然是正确的,但是如果eval块创建了一个对象,并且该对象在eval失败后超出了范围,那么将在{{1}之前调用该对象的DESTROY方法声明。如果if恰好在没有本地化DESTROY的情况下调用eval并且成功,那么当您的$@语句运行时,if变量将被清除。

解决这些问题的方法是:

$@

逐行分开,my $return = do { local $@; my $ret; eval {$ret = this_could_fail(); 1} or die "eval failed: $@"; $ret }; local $@块创建一个新的$@,以防止破坏以前的值。 do将是评估代码的返回值。在eval块中,my $ret被分配给,然后块返回$ret。这样,无论如何,如果eval成功,它将返回true,如果失败则返回false。在失败的情况下,由您决定该怎么做。上面的代码就好了,但你可以很容易地使用eval块的返回值来决定运行其他代码。

由于上述咒语有点乏味,因此容易出错。使用像1这样的模块可以使您免受这些潜在错误的影响,而每个eval会增加一些函数调用。了解如何正确使用eval非常重要,因为如果必须使用字符串eval,Try::Tiny将无法帮助您。

答案 1 :(得分:11)

答案 2 :(得分:6)

除了上面的答案,我还会添加......

  • eval会受到导致远程操作的全局$SIG{__DIE__}处理程序的影响。
  • 新手很容易混淆eval BLOCKeval STRING,因为它们似乎做同样的事情,但其中一个是安全漏洞。

尝试:: Tiny有自己的陷阱,最大的问题是虽然它看起来像一个块,但它实际上是一个子程序调用。这意味着:

eval {
    ...blah blah...
    return $foo;
};

和此:

try {
    ...blah blah...
    return $foo;
};

不要做同样的事情。这些都列在CAVEATS section of the Try::Tiny docs中。也就是说,我推荐它eval

答案 3 :(得分:0)

在X11上使用eval功能可能仍然无法保持活动状态。

代码就像

eval {    
    @win_arrays = GetWindowsFromPid($pid);
};

该脚本将退出

  

X请求失败的错误:...