2011-09-29 45 views
15

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

+0

的[为什么'$可能重复@ '不可信?](http://stackoverflow.com/questions/3686857/why-is-untrustworthy),[Perl中有什么坏例外](http://stackoverflow.com/questions/2165161/) – mob

+2

唯一回覆ason你不会使用内建的是你没有运行当前版本的Perl。 – tchrist

+0

@mob - 是的,这似乎是同一个问题。 – Hugh

回答

25

Perl的eval有两种版本,字符串以及eval块EVAL。字符串eval调用编译器来执行源代码。 Block eval包围了已包装在代码中的代码,该代码将捕获die异常。 (字符串eval还捕获die异常,以及任何编译错误)。

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

每当您拨打eval时,它将更改[email protected]的值。如果eval成功或者由eval捕获到错误,它将是''

这意味着,只要您调用eval,您将清除以前的任何错误消息。 Try::Tiny为您定制了[email protected]变量,以便成功的eval不会清除之前失败的eval的消息。

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

eval {...}; 
if ([email protected]) { 
    # deal with error here 
} 

这依赖于两个假设,第一,任何错误信息[email protected]可以包含是一个真值(通常为真),并且存在的eval块和if语句之间没有任何代码。

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

的解决这些问题的是:

my $return = do { 
    local [email protected]; 
    my $ret; 
    eval {$ret = this_could_fail(); 1} or die "eval failed: [email protected]"; 
    $ret 
}; 

打破了由线,除了线,所述local [email protected]do块这防止重挫先前值创建一个新的[email protected]my $ret将作为评估代码的返回值。在eval块中,$ret被分配给,然后该块返回1。这样,无论如何,如果eval成功,它将返回true,如果失败,它将返回false。在失败的情况下,你应该怎么做。上面的代码只是死掉了,但你可以很容易地使用eval块的返回值来决定运行其他代码。

由于上面的咒语有点乏味,因此容易出错。使用像Try::Tiny这样的模块可以避免潜在错误,但需要花费几个函数调用每个eval。知道如何正确使用eval非常重要,因为如果您必须使用字符串eval,Try::Tiny不会帮助您。

+4

已在当前版本中修复。 – tchrist

+0

小修正 - 如果没有抛出异常,'$ @'实际上是一个空字符串而不是undef。 –

+1

@Grant McLean =>谢谢,我应该记住,因为这就是我平常的repl处理错误的方式:'perl -wE'说eval,$ @ while <>'' –

6

除了上述问题的答案,我想补充...

  • EVAL通过在距离全球$SIG{__DIE__}处理造成动作的影响。
  • 新手很容易混淆eval BLOCKeval STRING,因为它们似乎做同样的事情,但一个是安全漏洞。

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

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

这:

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

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

+2

你是说'eval' *仍然*无法在5.14中破解? **真的吗?**这会令人非常失望,因为我知道很多工作都是为了试图通过修复困扰eval的任何潜在错误来渲染'Try :: Tiny'。如果这种努力失败了,那么存在一个可怕的问题,因为'Try :: Tiny'仍然只是一个CPAN模块,而不是核心。如果你无法做到真正的工作,并且可靠地完成核心工作,那么这是一个不可接受的情况。 – tchrist

+1

@tchrist忘记了这一点。据我了解,5.14.0修复了一类与$ @和对象销毁之间的交互有关的bug,并且通常会产生'eval {...};如果($ @){...}'更可靠。我相信解决了Try :: Tiny 3个问题中的2个问题......而第三个问题(假的$ @)是不太可能的。它仍然留下我提到的观点。在eval内部放置'$ SIG {__ DIE __}'是一个不错的特性。并拨打电视剧,哥们。 – Schwern

+0

调用'eval STRING'是一个“安全问题”不仅过于戏剧化;它甚至不是真的。自从23年前它首次出现在perl2以来,我使用了'eval STRING',我可以向你保证,我从来没有遇到过任何所谓的“安全问题”。当然,愚蠢的程序员可以用它做一些愚蠢的事情,但几乎任何事情都是如此。如果你存在于一个病态偏执的世界,你应该使用污染模式和/或安全隔间。在一个普通的世界里,'eval STRING'完成了很多有用的工作;请参阅经典*重命名*程序。 – tchrist

0

对X11函数使用eval可能仍然无法保持活动状态。

代码如下

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

的脚本将被从失败的请求

X错误退出:...

相关问题