非root用户没有权限写 /usr/lib/perl5,所以编译Perl模块时必须将PREFIX设置到自己的主目录下。使用CPAN时可以这样设置:
第一次启动CPAN,执行设置时,以下问题回答 PREFIX=~/perl:
Parameters for the 'perl Makefile.PL' command?
Typical frequently used settings:
PREFIX=~/perl non-root users (please see manual for more hints)
Your choice: [INSTALLDIRS=site] PREFIX=~/perl
然后编辑~/.bashrc,添加这样一行,然后重新登录或直接执行 source ~/.bashrc。否则CPAN中的make test会出错。
export PERL5LIB=`echo ~/perl/*/perl/* | sed 's/ /:/g'`
在Perl的各种文件测试运算符中,最有用的运算符之一就是 -C 了。 它返回的是文件的ctime到程序启动的那一时刻经过的天数。 如果不是整数天,返回值就带有小数。
这个运算符经常用来检测文件是否过期,比如判断文件距离上次修改是否超过了3天, 如果超过就将其删除。但是,你是否真正理解 -C 的工作原理呢?
阅读全文 »1. 脱引用(dereference)并不是只能用在引用上,返回引用的函数也能使用。如:
sub foo { my $a = 10; return \$a; }
print ${foo()}; # 返回10
print "${foo()}"; # 甚至在字符串替换中也能用!
2. 两个相邻的下标之间可以省去 -> 。如:
print $sue{children}->[1]->{age};
print $sue{children}[1]{age}; # 与上一条语句相同
3. 将字符串当作变量名的行为叫做符号引用:
$a = "x"; $x = 10; print $$a # 输出10
注意符号引用只能引用到全局变量,用my声明过的就引用不到了。使用use strict可以禁止符号引用。
阅读全文 »之前曾经介绍过 SmallProf 和 DProf。 之所以说NYTProf 是最强测试工具,是因为它给出了极其详尽的测试信息, 并且整理成便于阅读的HTML格式,将函数按着执行速度排序,而且测试速度相当快。
fcicq@2008/12/30:NYTProf是 New York Times Profiler的意思,想不到吧~
测试方法如下:
$ perl -d:NYTProf testfile.pl
执行后生成nytprof.out文件,执行以下命令将其转换成html:
$ nytprofhtml
然后打开nytprof/index.html,尽情查看测试结果吧。 结果中几乎每一项都有说明,这里就不多说了。

代码写多了,程序就会变得臃肿;程序臃肿了,就会变慢。 这时提高代码执行效率就非常重要了。 但是,代码优化并不是几条best practice就能完成的。 那些无关痛痒的空间分配、减少复制等优化措施,虽然有效,但却微乎其微。 优化的关键,是要找出瓶颈并解决之,这样才能以最小的代价获得最佳的效果。
这就用到Perl的一个强大的工具:DProf。 它可以测定程序执行的每个函数所花费的时间, 通过它,你可以迅速找到瓶颈在什么地方,再对症下药。
最近我做的一次性能分析是这样的。 我们的系统在某种条件下发送邮件时特别慢, 甚至等待十几分钟也无法结束。究竟问题出在哪里却不得而知。 于是DProf上场了。
首先写了个脚本,专门调用发送邮件的功能,排除其他功能的影响。 然后运行命令:
$ perl -d:DProf ./sendmail.pl
运行结束之后,会在当前目录下生成一个tmon.out文件。 使用dprofpp即可查看统计信息:
$ dprofpp Total Elapsed Time = 19.70195 Seconds User+System Time = 18.52195 Seconds Exclusive Times %Time ExclSec CumulS #Calls sec/call Csec/c Name 83.5 15.46 15.770 11 1.4059 1.4337 Foo::Bar::crypt 9.34 1.730 1.730 2 0.8650 0.8650 Net::Cmd::datasend 1.24 0.230 0.305 17459 0.0000 0.0000 Crypt::Blowfish::encrypt 0.70 0.129 0.818 36 0.0036 0.0227 base::import 0.59 0.110 0.709 24 0.0046 0.0296 Foo::Bar::FooBarList::BEGIN 0.44 0.082 0.082 9108 0.0000 0.0000 IO::Wrap::read 0.40 0.075 0.075 17647 0.0000 0.0000 Crypt::Blowfish::crypt 0.27 0.050 0.887 7 0.0071 0.1268 main::BEGIN
统计信息的意思分别是:
- ExclSec: 函数自身的执行时间(不包括函数调用的其他函数)
- CumulS: 函数的执行时间(包括函数调用的其他函数)
- #Calls: 调用次数
- sec/call: ExceSec/#Calls
- Csec/c: CumulS/#Calls
- Name: 函数名称
从上面的统计信息中可以看出,Foo::Bar::crypt占用了最多的时间(83.5%)。 而且更为重要的信息是,CumulS - ExclSec = 15.770 - 15.46 = 0.31, 也就是说,绝大部分时间都耗在了 Foo::Bar::crypt 函数本身,而不是它调用的其他函数。 这样,只需针对Foo::Bar::crypt函数进行分析就可以了。
还可以使用另一个性能测试工具Devel::SmallProf。
| 类型 | 标量环境 | 列表环境 |
| 标量'hello' | 标量本身 | 标量本身 |
| 列表('a','b','c') | 最后一个标量'c' | 列表本身 |
| 数组 | 数组中的元素个数 | 数组元素的列表 |
| 散列 | 斜线分隔的已用空间和分配的总空间的值组成的字符串 | 键值对的列表 |
| 行输入操作符<FH> | 文件的一行 | 文件剩余所有行组成的列表 |
| 文件名glob <*.xml> | 一个文件名 | 所有文件名的列表 |
| 范围操作符 .. | 左操作数为真时,返回真,直到右操作数为真后,返回假 | 左操作数到右操作数之间的所有值 |
| each %hash | 散列的一个键 | 散列的一个键值对 |
| gmtime | ctime(3)风格的字符串 | struct tm结构的9个值 |
| localtime | ctime(3)风格的字符串 | struct tm结构的9个值 |
| grep EXPR LIST | grep表达式匹配的次数 | 匹配表达式的所有值 |
| keys %hash | 散列中键的数目 | 散列的所有键 |
| m// | 表示匹配是否成功的布尔值 | 捕获的$1、$2、$3...的列表 |
| m//g | 执行一次匹配并返回表示是否成功的布尔值 | 反复执行匹配,返回所有捕获组成的列表 |
| readdir | 一个文件名 | 剩余所有文件名的列表 |
| readline | 文件的一行 | 文件剩余所有行组成的列表 |
| reverse LIST | 连接LIST中的所有元素成字符串,然后将这个字符串的字符顺序反转 | LIST的反转列表 |
| split | 分割结果的子字符串的数量 | 分割的子字符串 |
| stat | 表示执行是否成功的布尔值 | 文件的统计信息(包含13个元素的列表) |
相信很多人都写过下面这种函数,把参数当作hash引用来赋值:
sub foo {
my $hash = shift;
$hash->{foo} = 'bar';
}
然后这样调用:
foo($a);
调用之后,$a就变成了一个hash引用,里面包含了 foo => 'bar' 这一对值。 当然你可能会说,干嘛要在foo里面修改参数,直接return $hash不行吗。 当然可以,而且我也推荐使用return的方式, 不过有时候foo函数可能会很复杂,或者由于其他的原因而不得不使用修改参数的方式。
且慢!这种用法有时候会会失败。看下面的例子:
#!/usr/bin/perl
use Data::Dumper;
sub foo {
my $h = shift;
$h->{hello} = 'World!';
}
my $a;
my $b = {};
foo($a); print Dumper($a);
foo($b); print Dumper($b);
运行结果:
$VAR1 = undef;
$VAR1 = {
'hello' => 'World!'
};
为什么$a执行foo后仍然为undef,而$b就有值?
这是因为,my $a; 没有对$a初始化,此时$a的值为undef。 而调用foo时,参数实际上是传值调用,$a的undef值被赋给了$h变量, 此时$h = undef。而下一行将$h作为hash引用赋值时,系统会自动为$h赋一个空引用作为初始值。 ——但是,这个空引用以及接下来赋给的 hello => 'World!' 的值不会传回给外面的 $a,因为是值传递!
而$b则不同,初始化成一个空hash引用,这样传递给foo的就是个真正的引用值, 这样 hello => 'World!'的赋值可以赋给该引用。
所以,在使用hash引用时,初始化是非常必要的。
今天讨论一下m//g一个的小问题。m//g表示在字符串中查找所有可能出现的模式匹配。 在标量环境下,m//g会依次查找每个出现的匹配。通常的用法是这样的:
while (/(pattern)/g) {
print $1; # 做些操作
}
在内部,m//g会保存一个“匹配位置”的变量,表示这次的m//g匹配到了什么位置,以便下次运行时从该处继续开始。 (这个位置可以通过pos函数获得,这里就不多说了。)通常,用在while循环里是没有任何问题的,但如果换成if会怎样?
$_ = "abc"; print "a" if /a/g; print "b" if /b/g; # 输出结果:ab
结果是正确的。如果将语句换个位置呢?
$_ = "abc"; print "b" if /b/g; print "a" if /a/g; # 输出结果:b
可见,第二次的模式 a 没有匹配。其实可以想到,第一次的m/b/g匹配后,匹配位置已经指向了字符串中的下一个位置c, 第二行的m/a/g继续从c开始匹配,当然不会匹配到任何东西。
借用一下《精通正则表达式》第7章对于Match运算符的说明:
| 匹配类型 | 尝试开始位置 | 匹配成功时的pos值 | 匹配失败时的pos值 |
| m/.../ | 字符串起始位置(忽略pos) | 重置为undef | 重置为undef |
| m/.../g | 字符串的pos位置 | 匹配结束位置的偏移值 | 重置为undef |
| m/.../gc | 字符串的pos位置 | 匹配结束位置的偏移值 | 不变 |
话说回来,在if中使用/g选项是毫无意义的。这里只是提个醒,当你遇到诡异的无法匹配的问题时,别忘了是不是在if中使用了m//g。
昨天一个同事问我关于Perl中的 -| 描述符的问题。 他的程序大概是这样的:
unless (open FH, "-|") {
exec "foo bar"; # 用exec执行另一个程序
exit;
}
while (<fh>) {
...
}
close FH;
$ret = $? >> 8;
if ($ret == 1) {
...
}
那么这里的 open FH, "-|" 是什么意思?$? >> 8 又是什么意思?
阅读全文 »/fh
在Perl中解析XML的方法最常见的就是使用 XML::DOM 和 XML::Simple了。 XML::DOM过于庞大,而且解析结果是一个DOM树,操作也不方便。 对于小型且不复杂的XML文件,XML::DOM真是杀鸡用牛刀。 这时就轮到轻便的XML::Simple上场了。
XML::Simple如其名,真的很简单。假设XML内容如下:
<opt>
<user login="grep" fullname="Gary R Epstein" />
<user login="stty" fullname="Simon T Tyson" >
<session pid="12345"/>
</user>
<text>This is a test.</text>
</opt>
那么只需这样写:
use XML::Simple;
use Data::Dumper;
$xml = XMLin('sample.xml');
print Dumper($xml);
就可以轻而易举地将XML解析成一个hash,然后用foreach依次处理即可。
阅读全文 »