Perl - 错误处理

执行和错误总是一起发生的。 如果您正在打开一个不存在的文件。 那么如果你没有正确处理这种情况,那么你的程序就会被认为质量很差。

如果发生错误,程序将停止。 因此,适当的错误处理用于处理程序执行过程中可能发生的各种错误,并采取适当的措施而不是完全停止程序。

您可以通过多种不同方式识别和捕获错误。 在 Perl 中捕获错误然后正确处理它们非常容易。 这里有一些可以使用的方法。


if 语句

if 语句是当您需要检查语句的返回值时显而易见的选择; 例如 −

if(open(DATA, $file)) {
   ...
} else {
   die "Error: Couldn't open the file - $!";
}

这里变量 $! 返回实际的错误信息。 或者,在有意义的情况下,我们可以将语句简化为一行; 例如 −

open(DATA, $file) || die "Error: Couldn't open the file $!";

unless 函数

unless 函数与 if 逻辑相反:语句可以完全绕过成功状态,只有在表达式返回 false 时才会执行。 例如 −

unless(chdir("/etc")) {
   die "Error: Can't change directory - $!";
}

unless 语句最适用于您希望仅在表达式失败时引发错误或替代的情况。 该语句在单行语句中使用时也有意义 −

die "Error: Can't change directory!: $!" unless(chdir("/etc"));

在这里,只有在 chdir 操作失败时我们才会死,而且它读起来很好。


三元运算符

对于非常短的测试,您可以使用条件运算符 ?:

print(exists($hash{value}) ? 'There' : 'Missing',"\n");

这里不太清楚我们要达到什么目的,但效果与使用 ifunless 语句相同。 当您想要快速返回表达式或语句中的两个值之一时,最好使用条件运算符。


warn 函数

warn 函数只是发出一个警告,一条消息被打印到 STDERR,但没有采取进一步的行动。 因此,如果您只想为用户打印警告并继续其余操作,则它会更有用 −

chdir('/etc') or warn "Can't change directory";

die 函数

die 函数的工作方式与 warn 类似,只是它也调用 exit。 在普通脚本中,此函数具有立即终止执行的效果。 您应该使用此功能,以防程序出现错误时继续无用 −

chdir('/etc') or die "Can't change directory";

模块内的错误

我们应该能够处理两种不同的情况 −

  • 在引用模块文件名和行号的模块中报告错误 - 这在调试模块时很有用,或者当您特别想要引发与模块相关的错误时,而不是与脚本相关的错误。

  • 在引用调用者信息的模块中报告错误,以便您可以在脚本中调试导致错误的行。 以这种方式引发的错误对最终用户很有用,因为它们会突出显示与调用脚本的起始行相关的错误。

warndie 函数的工作方式与从模块中调用时的预期略有不同。 例如,简单的模块 −

package T;

require Exporter;
@ISA = qw/Exporter/;
@EXPORT = qw/function/;
use Carp;

sub function {
   warn "Error in module!";
}
1;

从如下脚本调用时 −

use T;
function();

它将产生以下结果 −

Error in module! at T.pm line 9.

这或多或少是您可能期望的,但不一定是您想要的。 从模块程序员的角度来看,这些信息很有用,因为它有助于指出模块本身的错误。 对于最终用户来说,所提供的信息是毫无用处的,而对于除了经验丰富的程序员之外的所有人来说,这完全是毫无意义的。

此类问题的解决方案是 Carp 模块,它提供了一种简化的方法来报告模块内返回有关调用脚本的信息的错误。 Carp 模块提供了四个函数:carp、cluck、croak 和confession。 这些功能将在下面讨论。


carp 函数

carp 函数与 warn 基本等价,它会将消息打印到 STDERR,而无需实际退出脚本并打印脚本名称。

package T;

require Exporter;
@ISA = qw/Exporter/;
@EXPORT = qw/function/;
use Carp;

sub function {
   carp "Error in module!";
}
1;

当从如下脚本调用时 −

use T;
function();

它将产生以下结果 −

Error in module! at test.pl line 4

cluck 函数

cluck 函数是一种增压鲤鱼,它遵循相同的基本原理,但也会打印导致调用该函数的所有模块的堆栈跟踪,包括原始脚本的信息。

package T;

require Exporter;
@ISA = qw/Exporter/;
@EXPORT = qw/function/;
use Carp qw(cluck);

sub function {
   cluck "Error in module!";
}
1;

从如下脚本调用时 −

use T;
function();

它将产生以下结果 −

Error in module! at T.pm line 9
   T::function() called at test.pl line 4

croak 函数

croak 函数等价于die,除了它向上一级报告调用者。 和 die 一样,这个函数在向 STDERR 报错后也会退出脚本 −

package T;

require Exporter;
@ISA = qw/Exporter/;
@EXPORT = qw/function/;
use Carp;

sub function {
   croak "Error in module!";
}
1;

从如下脚本调用时 −

use T;
function();

它将产生以下结果 −

Error in module! at test.pl line 4

与 carp 一样,根据 warn 和 die 功能,同样的基本规则适用于包含行和文件信息。


confess 函数

confess功能类似于cluck; 它调用 die,然后一直打印到原始脚本的堆栈跟踪。

package T;

require Exporter;
@ISA = qw/Exporter/;
@EXPORT = qw/function/;
use Carp;

sub function {
   confess "Error in module!";
}
1;

从如下脚本调用时 −

use T;
function();

它将产生以下结果 −

Error in module! at T.pm line 9
   T::function() called at test.pl line 4