在perl脚本运行时处理错误及警告
在日常脚本编写及运行过程中,总会出现这样或那样的错误或警告,当然并非没有消息就是好消息,我们需要对脚本进行改进。我们可以使用'warnings'来严格规范编写过程,以便在测试阶段就将那些潜在的bug报出来;如果是第三方使用,不需要将这些信息报到用户的终端上,那我们需要将其重定向的日志文件中,以备后查。Perl内置了两个重要的且简单的处理函数:warn与die。
warn 函数
warn 函数用于触发一个警告信息,不会有其他操作,输出到 STDERR(标准输出文件),通常用于给用户提示:
chdir('/etc') or warn "无法切换目录";
die 函数
die 函数类似于 warn, 但它会执行退出。一般用作错误信息的输出:
chdir('/etc') or die "无法切换目录";
Carp 模块
在 Perl 脚本中,报告错误的常用方法是使用 warn() 或 die() 函数来报告或产生错误。而对于 Carp 模块,它可以对产生的消息提供额外级别的控制,尤其是在模块内部。标准 Carp 模块提供了 warn() 和 die() 函数的替代方法,它们在提供错误定位方面提供更多信息,而且更加友好。当在模块中使用时,错误消息中包含模块名称和行号。
carp 函数
carp函数可以输出程序的跟踪信息,类似于 warn 函数,通常会将该信息发送到 STDERR。
cluck 函数
cluck() 与 warn() 类似,提供了从产生错误处的栈回溯追踪。
croak 函数
croak() 与 die() 一样,可以结束脚本。
confess 函数
confess() 与 die() 类似,但提供了从产生错误处的栈回溯追踪。
Catch the warnings before they are printed to the screen.
Signals-信号
Perl has a built-in hash called %SIG, in which the keys are the names of the signals available in you operating system. The values are subroutines (more specifically references to subroutines), that will be called when the specific signal arrives.
使用Perl内建的变量'%SIG'来记录,为不同的信号来定制相关的处理函数引用。
In addition to the standard signals of your operating system Perl added two internal "signals". One of them is called __WARN__ and is triggered every time some code calls the warn() function. The other one is called __DIE__ and it is triggered when die() is called.
当程序出现警告或错误时,就会触发相关的函数(warn() or die())。
Anonymous subroutines-匿名函数
sub { } is an anonymous subroutine, that is, a subroutine that does not have a name but it has a body. (In this example even the body, the block, is empty but I hope you get the point.)
Capture warnings - do nothing-仅捕获警告
If you added code like this:
local $SIG{__WARN__} = sub {
# here we get the warning
};
You effectively said that every time there is a waning anywhere in the code - don't do anything. Basically you hide all the warnings.
Capture warnings - and turn them into exceptions-捕获警告并处理
You could also write:
local $SIG{__WARN__} = sub {
die;
};
Which would call die() whenever a warning happened, Which means you turn every warning into an exception.
If you also wanted to keep the warning message in that exception you could write:
local $SIG{__WARN__} = sub {
my $message = shift;
die $message;
};
这将把具体的警告信息作为首参传入函数处理。
Capture warnings - and log them-捕获警告并记录
Make the warnings less noisy but keep them for later inspection:
local $SIG{__WARN__} = sub {
my $message = shift;
logger($message);
};
这里调用了'logger()'函数来记录相关的信息。
Logging-日志记录
Hopefully your application already has a logging mechanism. If not, this might be a good reason to add one. Even if you cannot add one, you might be able to use the built-in logging mechanism of your operating system. That means syslog on Linux and Event Logger on MS Windows.
In our example we use a simple home made logger() function just to represent the idea.
Full example capturing and logging warnings
下面是一个捕获并记录的示例:
use strict;
use warnings;
local $SIG{__WARN__} = sub {
my $message = shift;
logger('warning', $message);
};
my $counter;
count();
print "$counter\n";
sub count {
$counter = $counter + 42;
}
sub logger {
my ($level, $msg) = @_;
if (open my $out, '>>', 'log.txt') {
chomp $msg;
print $out "$level - $msg\n";
}
}
上述代码将在执行目录的日志文件(log.txt)中有如下记录行:
warning - Use of uninitialized value $counter in addition (+) at log.pl line 14.
Warning in the warn handle-编写处理程序
One lucky aspect of __WARN__ is that when the code in the __WARN__ handle is executed it is automatically disabled. So warnings in a warn handle won't cause an infinite loop.
Avoid multiple warnings-避免更多的警告
当脚本出现大量相同的出错或警告信息时,而我们只想保留那些重复信息中的一条即可。
use strict;
use warnings;
my %WARNS;
local $SIG{__WARN__} = sub {
my $message = shift;
return if $WARNS{$message}++;
logger('warning', $message);
};
my $counter;
count();
print "$counter\n";
$counter = undef;
count();
sub count {
$counter = $counter + 42;
}
sub logger {
my ($level, $msg) = @_;
if (open my $out, '>>', 'log.txt') {
chomp $msg;
print $out "$level - $msg\n";
}
}
当我们重置'$counter'变量为'undef'时,再次调用'count()'函数将会再次生成与上次相同的警告信息。
工作原理:
Before calling the logger we check if the current string is in the %WARNS hash. If it isn't, we add it and then call logger(). If it was already there, we just call return and don't log the event a second time.
You might recall, the same idea was used when we wanted to have unique values in an array.
其实是将警告字串作为hash的key,然后对key的value进行计数,来判断之前出现的敬告或错误是否出现过。
What is this local-为什么使用'local'范围
In all the examples you saw, I used the local function to localize the effect. Strictly speaking we did not need that in these examples as we were assuming the above code would be the first thing in your main script. In that case it does not matter as you are still in a global scope.
总而言之,使用'local'变量的行为更安全,脚本或模块间不会相互影响。
Avoiding the global %WARNS hash-避免使用全局'%WARNS'哈希
下面的代码要使用'Perl 5.10'及其以上的版本,引入'state'关键字,这样每次调用匿名函数函数时,计数值都不会发生改变。
use strict;
use warnings;
use v5.10;
local $SIG{__WARN__} = sub {
state %WARNS;
my $message = shift;
return if $WARNS{$message}++;
logger('warning', $message);
};
参考来源:
__WARN__ in perlvar