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

Hug*_*ugh 15 perl eval exception

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

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

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

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

解决这些问题的方法是:

my $return = do {
    local $@;
    my $ret;
    eval {$ret = this_could_fail(); 1} or die "eval failed: $@";
    $ret
};
Run Code Online (Sandbox Code Playgroud)

逐行拆分,为块local $@创建一个新的$@,以do防止破坏以前的值. my $ret将是评估代码的返回值.在eval块中,$ret分配给,然后块返回1.这样,无论如何,如果eval成功,它将返回true,如果失败则返回false.在失败的情况下,由您决定该怎么做.上面的代码就死了,但您可以轻松地使用eval块的返回值来决定运行其他代码.

由于上述咒语有点单调乏味,因此容易出错.使用类似的模块Try::Tiny将您与这些潜在的错误隔离开来,代价是每个eval会有更多的函数调用.了解如何正确使用eval非常重要,因为Try::Tiny如果必须使用字符串eval ,则不会对您有所帮助.

  • 已在当前版本中修复. (4认同)

Sin*_*nür 11

Try :: Tiny文档中解释了这些问题.简而言之,它们是:

  • 已在当前版本中修复. (2认同)

Sch*_*ern 6

除了上面的答案,我还会补充......

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

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

eval {
    ...blah blah...
    return $foo;
};
Run Code Online (Sandbox Code Playgroud)

还有这个:

try {
    ...blah blah...
    return $foo;
};
Run Code Online (Sandbox Code Playgroud)

不要做同样的事情.这些是在Try :: Tiny文档CAVEATS部分中列出的.那就是说,我推荐它eval.

  • 你是说'eval`在*5.14中仍然*不可用?**真的吗?**这将是*非常*令人失望,因为我知道很多工作都试图通过修复困扰`eval`的任何潜在错误来使`Try :: Tiny`过时.如果这种努力失败了,那么就会出现一个可怕的问题,因为`Try :: Tiny`仍然只是一个CPAN模块,而不是核心.如果你不能用核心做真正的工作,那么这是一个不可接受的情况. (2认同)