Perl中的错误处理最佳实践是什么?

15

我正在学习Perl,在很多示例中,错误的处理方式是这样的:

open FILE, "file.txt" or die $!;

在脚本中使用 die 来处理错误真的是最好的方式吗?


6个回答

22

在脚本中间使用die是否合适,这取决于你正在做什么。如果只是几十行代码,那么使用它也没问题。对于有几百行代码的小工具,可以考虑使用 confess (请参见下文)。如果是一个大型面向对象系统,包含许多类和相互连接的代码,则异常对象可能更好。

Carp 包中的 confess:
通常导致程序崩溃的 bug 不一定在报告 die 的那一行。 将 die 替换为 confess(请参见 Carp 包)将提供堆栈跟踪信息(我们如何到达此行),这极大地有助于调试。

对于处理 Perl 内置函数抛出的异常,我喜欢使用autodie。它捕获了 open 和其他系统调用的失败,并会为您抛出异常,无需编写 or die。这些异常可以使用 eval { } 捕获,或者更好的方式是使用Try::Tiny


1
我一直认为它们应该称之为 autoOrDie :) - friedo
1
@friedo 这样看起来太像 authorDie 了 :) - dolmen

15

由于我在几乎所有地方都使用 Log::Log4perl,所以我使用 $logger->logdie 代替 die 。如果你想更好地控制你的异常,请考虑使用 Exception::Class

最好使用Try::Tiny来捕获异常(请查看其文档以了解原因)。


7
除非你有更具体的想法,否则在发生意外情况时,是的,你希望死亡。
  • 无法打开文件并给出文件名而死亡要比系统告诉你无法从未定义的匿名文件中读取或写入更好。

  • 如果你谈论一个“脚本”,通常情况下你谈论的是一段相当简单的代码。不需要协调层次(通常不需要)。在 Perl 模块中,伴随着的观念是你不拥有执行环境,所以主要软件关心并在 eval 中捕获异常,或者根本不关心,死亡也是可以接受的。然而,你应该尝试让模块更加健壮,只需传回 undefs 或其他内容即可。

  • 你可以在 eval 块中捕获所有死亡(或 croaks),并在此处理更具体的问题。

  • 但如果你想检查 $!,那么编写该代码,你会得到更具体的解决方案。

  • 看看使用 strict 的几乎普遍标准。这是在可疑语法上死亡的代码,而不是让你继续进行。

所以我认为一般的想法是:是的,死亡,除非你有更好的处理方法。如果你有足够的远见,可以因为你知道不需要死亡而被原谅一两次。


3
更现代的方法是使用Carp标准库。
use Carp;
my $fh;
open $fh, '<', "file.txt" or confess($!);

主要优点是在程序崩溃时提供了堆栈跟踪。

嗯,除非您设置了其他 Carp 选项,否则 croak 不会提供堆栈跟踪。 - brian d foy
鲤鱼通常也不会坦白,但是在美国东海岸有一种叫做"croaker"的鱼。但是它们并不是鲤鱼。 - Paul Nathan
2
既然这是Perl,那么这种情况就显得非常合理。 ;) - Randy Levy
@SystematicFrank 当鲤鱼死亡时会发出咕咕声 - 它们比普通鱼更健谈。 - i alarmed alien

1

我使用die,但将其包装在eval块中以控制错误处理:

my $status = eval
{
    # Some code...
};

如果 'eval' 失败:

  1. $status 将未定义。
  2. $@ 将被设置为产生的任何错误消息(或 die 的内容)

如果 'eval' 成功:

  1. $status 将是块的最后一个返回值。
  2. $@ 将被设置为 ''。

5
eval {} 在使用过程中存在许多问题,需要使用复杂的解决方法。建议使用 Try::Tiny 替代它,Try::Tiny 将所有繁琐的代码封装在一个干净、易于使用的包装器中。 - daotoad
1
使用 eval {} 存在许多问题,需要使用晦涩的解决方法...如果你关心错误的识别。如果你无论出现什么原因都会采取相同的行动,那么一个简单的块级 eval 就可以了。但是,在所有其他情况下,请使用 Try::Tiny - Dave Sherohman
下次我写一些Perl的时候会试试这个方法。我使用这种方式来模拟try-catch(在eval块之后添加对$ @的检查)。感谢你的建议。 - Nick Gotch

0

正如@friedo所写,如果这是一个独立的几行脚本,die是可以的,但我认为在模块或require'd代码片段中使用die不是一个好主意,因为它会打断程序的流程。我认为流程控制应该是程序主体的特权。

因此,在模块中,我认为最好返回undef,例如return,它将在标量上下文中返回undef,在列表上下文中返回空列表,并设置一个异常对象以供获取更多详细信息。这个概念在模块Module::Generic中实现如下:

# in some module
sub myfunc
{
  my $self = shift( @_ );
  # some code...
  do{ # something } or return( $self->error( "Oops, something went wrong." ) );
}

然后,在调用者中,您将写入:

$obj->myfunc || die( "An error occurred at line ", $obj->error->line, " with stack trace: ", $obj->error->trace );

这里error将设置一个异常对象并返回undef。

然而,因为许多模块使用diecroak,您还需要使用eval或try-catch块(例如Nice::Try)来捕获这些中断。

完全披露:我是Module::Generic和Nice::Try的开发人员。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接