虽然建议在每个Perl脚本和模块中开启警告,可是你又不想用户看到Perl发出的警告。
一方面你想在代码前面使用use warnings作为你的安全网,另一方面,通常警告会出现在屏幕上。多数情况下,客户不知道如何处理这些警告。如果幸运的话这些警告仅仅让客户惊讶一下,当然,不幸的是他们尝试着去修复它们... (这里说的不是Perl程序员。)
第三方面,你或许想要保存这些警告供之后分析。
此外,在很多地方还有很多Perl脚本和应用程序没有使用use warnings也没有在#!行中使用-w。加上了use warnings就可能会产生大量的警告。
长远来看,当然是要消除这些警告,但是短期来说呢?
即便是长期计划,你也不能写出完全没有BUG的代码,你也不能确保应用将来永远不会打印出警告信息。
你能么?
你可以在警告打印到屏幕之前捕获它们。
信号
Perl有一个叫做%SIG的内建hash表,其中的键是操作系统信号的名字。对应的值是函数(大多数是函数引用),这些函数会在特定的信号触发时被调用。
除了系统提供的标准信号以外,Perl还添加了两个内部“信号”。其中一个是<h__warn__< span="">,它在每次代码调用warn()函数的时候触发。另外一个是__DIE__,它在每次调用die()时触发。
在本文中,我们会看到这些是怎样影响警告信息的。
匿名函数
sub { }是匿名函数,也就是一个只有函数体而没有名字的函数。(在这个例子中函数体也是空的,但是我希望你能明白我的意思。)
捕获警告--不处理
如果添加如下代码:
复制代码 代码如下:
local $SIG{__WARN__} = sub {
# 此处可以获得警告信息
};
这实际上表示每次程序的某个地方产生了警告信息时,不做任何处理。基本上,这会隐藏所有的警告。
捕获警告--并转换成异常
You could also write: 你也可以写成:
复制代码 代码如下:
local $SIG{__WARN__} = sub {
die;
};
这样会在每次产生警告的时候调用die(),也就是把每个警告转换成异常。
如果你想在异常中包含警告信息,可以这么写:
复制代码 代码如下:
local $SIG{__WARN__} = sub {
my $message = shift;
die $message;
};
实际的警告信息会作为唯一的参数传递给匿名函数。
捕获警告--并写入日志
你可能想在中间做些其他事情:
过滤嘈杂的警告信息,留待后来分析:
复制代码 代码如下:
local $SIG{__WARN__} = sub {
my $message = shift;
logger($message);
};
这里我们假设logger()是你实现的写日志函数。
写日志
假设你的应用程序已经有日志机制。如果没有的话,最好加上。即便你不能添加,你也需要操作系统的内建日志机制。例如Linux的syslog,MS Windows的Event Logger,其它操作系统也有它们内部的日志机制。
在本文的例子里,我们使用一个自制logger()函数来代表这个想法。
捕获并写日志的完整例子
复制代码 代码如下:
#!/usr/bin/perl
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文件中添加下面一行:
复制代码 代码如下:
Use of uninitialized value in addition (+) at code_with_warnings.pl line 14.
变量$counter和函数count()仅是产生警告示例的一部分。
警告处理函数中的警告信息
__WARN__在其处理函数执行过程中是自动被禁用的。所以在警告处理函数执行过程中产生的(新)警告信息不会导致无限循环。
你可以在perlvar文档中了解到更多细节。
Avoid multiple warnings
需要注意的是重复的警告信息可能会充斥日志文件。我可以使用一个简单的类似缓存的特性来减少重复警告信息的数量。
复制代码 代码如下:
#!/usr/bin/perl
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()函数来产生同样的警告。
我们也把__WARN__的处理函数替换成一个稍微复杂的版本:
复制代码 代码如下:
my %WARNS;
local $SIG{__WARN__} = sub {
my $message = shift;
return if $WARNS{$message}++;
logger('warning', $message);
};
在调用logger之前,会检查一下当前字符串是否已经在%WARNShash表中。如果没有的话,会添加它并调用logger()。如果已经有了,就调用return,并不二次记录同样的事件。
你可能回忆起我们在unique values in an array也使用了同样的点子。
local是什么?
在上面所有的例子中,我使用local函数來局部化(警告处理)效果。严格来说,在这些例子中我们没有必要这么做,因为假设这些代码是主脚本的第一部分。这种情况下就无所谓了,毕竟是在全局作用域里面。
然而,最好是这么用。
local对于在模块中限制(对警告)的改变是很重要的。特别是要发布的模块。如果没有局部化,会影响整个应用程序。limit则会把影响限制在所在的闭合代码块里。
避免使用全局的%WARNS
如果你正在使用Perl 5.10或者更新的版本,你可以改写一下代码来替换掉全局变量%WARNS。要这么做的话,需在脚本的开头使用use v5.10;,然后在匿名函数内部使用state关键词来声明变量。
复制代码 代码如下:
#!/usr/bin/perl
use strict;
use warnings;
use v5.10;
local $SIG{__WARN__} = sub {
state %WARNS;
my $message = shift;
return if $WARNS{$message}++;
logger('warning', $message);
};
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]