社区
最后更新于:2022-04-01 21:07:59
## Perl 基金会
Perl 基金会致力于推进 Perl 编程语言的开放讨论、协作、设计及编码。Perl 基金会是非盈利、501(c)(3) 组织。
[http://perlfoundation.org](http://perlfoundation.org/) [http://news.perlfoundation.org](http://news.perlfoundation.org/)
## Perl Mongers
世界各地的 Perl 用户组。
[http://www.pm.org](http://www.pm.org/)
## OSCON
奥莱理的开源大会。起初为 Perl 大会,后被扩展。
[http://conferences.oreilly.com](http://conferences.oreilly.com/)
## YAPC
又一个 Perl 大会。便宜、TPF 驱动以替代 OSCON。
[http://www.yapc.org](http://www.yapc.org/)
## IRC 频道
在线聊天频道。
Freenode (irc.freenode.net) 至少不让人讨厌。
MAGnet (irc.perl.org) 有最铁杆的 Perl 家伙,但最令人讨厌。
## 邮件列表
在 [http://lists.perl.org](http://lists.perl.org/) 有一打邮件列表。
## PerlMonks
一半是论坛,一半是问答。主要对新手有用。
[http://perlmonks.org](http://perlmonks.org/)
## Blog
Perl 用户 Blog。
[http://blogs.perl.org](http://blogs.perl.org/)
';
出版物
最后更新于:2022-04-01 21:07:57
## Perl 语言编程,第四版
称为大骆驼书,真正的 Perl 圣经。
## Perl 语言入门,第六版
称为小骆驼书,Perl 标准教程。
## Perl Cookbook,第二版
一本如何利用 Perl 干活的问题导向书籍。
## Object Oriented Perl
关于 Perl 5 面向对象编程的最佳解释。
## Perl 最佳实践
展示如何写优良、可维护的 Perl 代码。
## The Perl Review
在芝加哥出版,致力于 Perl 的杂志。
[http://theperlreview.com](http://theperlreview.com/)
';
开发工具
最后更新于:2022-04-01 21:07:54
## 剖析性能
使用 _Devel::NYTProf_ ,或 _Devel::DProf_ 。
## 分析代码质量
使用 _Perl::Critic_ ,它基本上是针对 Perl 的 lint。
## 分析变量结构
使用 _Data::Dumper_ 。
';
如何做…?
最后更新于:2022-04-01 21:07:52
## 如何验证 Email 地址是否有效
一般来说,你不能。有一些看起来合理的方法可以使用,但却没有办法检测地址 是否实际可以投递,如果没有实际尝试投递的话。
使用正则表达式:
~~~
# Match basically blah@blah.blah
if ( $addr =~ /^\S+\@\S+\.\S+$/ ) {
print "Looks OK";
}
~~~
如果你干真活的话,可能希望看看 CPAN 上可用的模块,比如: _Email::Address_ 、 _Email::Valid_ 。
## 如何从数据库获得数据
_DBI_ 及其 _DBD_ 子模块,如 _DBD::SQLite_ 。
## 如何从网页获得数据
_LWP_ 意为 _libwww-perl_ ,它是与网页交谈的标准方式。
_WWW::Mechanize_ 是使 HTML 处理更容易的 _LWP_ 的超集。
## 如何做日期计算
使用 _Date::Manip_ 、 _Date::Calc_ 、或 _DateTime_ 。全部都有不同的样式和 不同的能力。
## 如何处理程序的命令行参数
使用 _Getopt::Long_ 。
## 如何解析 HTML
无论你做什么,都不要使用正则表达式。使用 _HTML::Parse_ 或别的类似东东。 如果你解析 HTML 是为了从网页提取链接或图像,不妨使用 _WWW::Mechanize_ 。
## 如何来点颜色
使用 _Term::ANSIColor_ 。
## 如何读取键及不看到输入的密码
使用 _Term::ReadKey_ 。
';
陷阱
最后更新于:2022-04-01 21:07:50
## while` ()`
一定要小心这点。如果你不知怎么回事地得到了假值(如:空行),你的文件可能 停止处理了。假如你在处理文件读取(除非修改了 `$/`),这种事一般不会发生, 但却可能发生。
你更喜欢这样运行:
~~~
while (readdir(DIR)) {
~~~
假设你有文件名为 `0` 的话,那么程序将停止,且不会继续处理文件。
更合适的 `while` 循环看起来像这样:
~~~
while ( defined( my $line = ) ) {
while ( defined( my $file = readdir(DIR) ) ) {
~~~
';
性能
最后更新于:2022-04-01 21:07:48
Perl 让你干想干的事,包括很慢或内存消耗这样的事。此处将告诉你如何避免。
## 使用 `while` 而非 `for` 来迭代整个文件
代替读取文件的所有行并使用 `for` 处理数组,使用 `while` 一次仅读取一行。
这两个循环的功能相同:
~~~
for ( <> ) {
# do something
}
while ( <> ) {
# do something
}
~~~
差异是 `for` 将整个文件读到临时数组,而 `while` 一次读一行。对于小文件, 这可能不重要。但对于更大的文件,它可能吃掉你的所有可用内存。
在这种情况下使用 `while` 是一种好实践。
## 避免不必要的引起和字串化
除非绝对必要,不要引起大字符串:
~~~
my $copy = "$large_string";
~~~
这将创建两个 `$large_string` 的拷贝(一个是 `$copy`,而另一个是引起)。 然而:
~~~
my $copy = $large_string;
~~~
仅创建一个拷贝。
对于字串化大的数组也是一样:
~~~
{
local $, = "\n";
print @big_array;
}
~~~
这比下面的代码更内存高效:
~~~
print join "\n", @big_array;
~~~
或:
~~~
{
local $" = "\n";
print "@big_array";
}
~~~
';
风格
最后更新于:2022-04-01 21:07:45
## camelCase 很糟
你曾维护过别人的代码吗?你维护过像这样的代码吗?
~~~
my $variableThatContainsData =
someSubroutineThatMucksWithData( $someAwfulVariable );
~~~
混合大小写单词在 Perl 世界被称为 _camelCase_,通常它的令人不悦之处是使 阅读代码更难。
甚至具有糟糕名称的代码使用下划线也能变得更可读:
~~~
my $variable_that_contains_data =
some_subroutine_that_mucks_with_data( $some_awful_variable );
~~~
## warnings 和 strict
对于你希望维护、重用、及发布的任何程序,都应当具有下列代码行:
~~~
#!/usr/bin/perl
use strict;
use warnings;
~~~
启用 `strict` 使 Perl 抱怨不确定的代码结构,比如:未声明的变量、祼字、 以及软引用等。这些警告将导致 Perl 执行失败。
~~~
#!/usr/bin/perl
use strict;
$foo = 4; # undeclared variable error
$foo = Bar; # bareword error
my $bat = "foo";
print $$bat; # reference error
~~~
启用 `warnings` 使 Perl 甚至抱怨更多东东。但不像 `strict`,这些抱怨在 一般条件下并不严重。
~~~
#!/usr/bin/perl
use warnings;
$a + 0; # void context warning
# name used once warning
# undef warning
print "program continued\n"; # prints
~~~
如果你想要 `warnings` 变得严重,告诉它:
~~~
use warnings FATAL => 'all';
$a + 0; # void warning and then exits
print "program continued\n"; # doesn't print
~~~
## 使用 `perltidy` 格式化 Perl 源代码
选择何种代码风格是仁者见仁,智者见智的事情。但重要的是保持风格的一致性。 为了使格式化 Perl 源代码更容易,你可以使用 [Perl::Tidy](https://metacpan.org/release/Perl-Tidy) 模块随付的 `perltidy` 工具。
例如,使用 _Perl 最佳实践_ 一书所推荐的风格来格式化源代码:
~~~
$ perltidy -pbp myprogram.pl -o myprogram.formated.pl
~~~
';
高级函数
最后更新于:2022-04-01 21:07:43
## 上下文与 wantarray
Perl 有三种上下文:空、标量、以及列表。
~~~
func(); # void
my $ret = func(); # scalar
my ($ret) = func(); # list
my @ret = func(); # list
~~~
如果你在子例程或 `eval` 块中,你能够使用 `wantarray` 来决定想要的上下文。
以下是处理正则表达式返回值的上下文例子:
~~~
my $str = 'Perl 101 Perl Context Demo';
my @ret = $str =~ /Perl/g; # @ret = ('Perl','Perl');
my $ret = $str =~ /Perl/g; # $ret is true
~~~
## `..` 和 `...`
这些叫区间操作符,它们能够帮助代码处理整数或字符区间。
在下例中,`@array` 是手动填充的。这些是等价的:
~~~
my @array = ( 0, 1, 2, 3, 4, 5 );
my @array = 0..5;
~~~
当用于此种方式时,`..` 和 `...` 是等效的。
区间操作符只能增加。这会产生一个空列表:
~~~
my @array = 10..1; # @array is empty
~~~
如果你想要逆向,要求它。
~~~
my @array = reverse 1..10; # @array descends from 10 to 1
~~~
你也可以在标量上下文中使用区间操作符,但那超出了本节的范围。参阅手册页 了解细节。
';
命令行选项
最后更新于:2022-04-01 21:07:41
## Shebang 行
几乎每个 Perl 程序都如此开始:
~~~
#!/usr/bin/perl
~~~
这是 UNIX 结构,它告诉 Shell 直接执行余下的输入程序文件。
你可以在此行添加 Perl 的任何命令行选项,它们将成为选项之后命令行的一部分。 如果你有一个程序包含:
~~~
#!/usr/bin/perl -T
~~~
然后执行:
~~~
perl -l program.pl
~~~
`-l` 和 `-T` 两个选项都会使用,但 `-l` 将先用。在 _perlrun_ 文档中介绍 了 Perl 的命令行选项。此处只介绍最有用的内容。
## perl -T
Perl 允许你在 `taint` 模式执行。在此模式中,变量在使用前需要“消毒”,以 应对不安全的操作。
何为不安全?
* 执行程序
* 写入文件
* 创建目录
* 基本上,修改系统的任何事情
如果你没有“去污”数据,那么这些操作将是程序中的严重错误。
如何去污?使用正则表达式匹配有效的值,然后将匹配赋给变量。
~~~
my ($ok_filename) = $filename =~ /^(\w+\.log)$/;
~~~
你应当达到程序 `taint` 安全的目的。
## perl -c file.{pl,pm}
此命令行选项允许检查给定文件的语法错误。它也会执行 `BEGIN` 块中的任意 代码,并检查程序中已使用的模块。
你应当使用 `-c` 在每次更改后检查代码的语法。
## perl -e 'code'
该选项允许你从命令行执行代码,以代替将程序写入文件来执行。
~~~
$ perl -e 'print "1\n"'
1
~~~
这对小程序、快速计算、以及与其他选项组合使用非常有用。
## -n、-p、-i
Perl 的 `-n` 选项允许你针对标准输入的每行重复执行代码(通常使用 `-e` 指定)。 这些是等效的:
~~~
$ cat /etc/passwd | perl -e 'while (<>) { if (/^(\w+):/) { print "$1\n"; } }'
root
...
$ cat /etc/passwd | perl -n -e 'if (/^(\w+):/) { print "$1\n" }'
root
...
~~~
`-p` 选项与 `-n` 相同,除了它在每行后打印 `$_`。
如果你组合 `-i` 选项,Perl 将就地编辑你的文件。因此,要将一堆文件从 DOS 转换成 UNIX 换行,你可以这样干:
~~~
$ perl -p -i -e 's/\r\n/\n/' file1 file2 file3
~~~
## perl -M
Perl 的 `-M` 选项使你可以从命令行使用模块。有好些模块首选此方式运行(如 _CPAN_和 _Devel::Cover_)。如果你需要使用 `-e` 包含模块,它也是习惯的 简写。
~~~
$ perl -e 'use Data::Dumper; print Dumper( 1 );'
$VAR1 = 1;
$ perl -MData::Dumper -e 'print Dumper( 1 );'
$VAR1 = 1;
~~~
## 明白模块是否已被安装
试试从命令行加载模块。`-e1` 只是一个立即退出的空程序。如果你获得错误, 那么该模块未被安装:
~~~
$ perl -MWWW::Mechanize::JavaScript -e 1
Can't locate WWW/Mechanize/JavaScript.pm in @INC...
BEGIN failed--compilation aborted.
$
~~~
返回没有错误则意味着该模块已安装。
~~~
$ perl -MWWW::Mechanize -e 1
$
~~~
当它存在时,检查版本:
~~~
$ perl -MWWW::Mechanize -e'print $WWW::Mechanize::VERSION'
~~~
并非所有模块都有 `$VERSION` 变量,因此这可能不总是工作。
';
特殊变量
最后更新于:2022-04-01 21:07:39
## $_
`$_` 是默认变量。它常用于内置函数的默认参数。
~~~
while ( <> ) { # Read a line into $_
print lc; # print lc($_)
}
~~~
这与下列代码相同:
~~~
while ( $it = <> ) {
print lc($it);
}
~~~
## $0
`$0` 包含执行程序的名称,正如给 Shell 的一样。如果程序直接通过 Perl 解释器执行,那么 `$0` 包含文件名称。
~~~
$ cat file.pl
#!/usr/bin/perl
print $0, "\n";
$ ./file.pl
file.pl
$ perl file.pl
file.pl
$ perl ./file.pl
./file.pl
$ cat file.pl | perl
-
~~~
`$0` 是 C 程序员期望从 `argv` 数组找到的第一个元素。
## @ARGV
`@ARGV` 包含给程序的参数,顺序与 Shell 中一样。
~~~
$ perl -e 'print join( ", ", @ARGV), "\n"' 1 2 3
1, 2, 3
$ perl -e 'print join( ", ", @ARGV), "\n"' 1 "2 3" 4
1, 2 3, 4
~~~
C 程序员可能会搞混,因为 `$ARGV[0]` 是他们的 `argv[1]`。不要犯这样的错。
## @INC
`@INC` 包含 Perl 搜索模块的所有路径。
Perl 程序员通过后置或前置到 `@INC` 添加库路径。眼下,使用 `use lib` 代替。 下面的代码等效:
~~~
BEGIN { unshift @INC, "local/lib" };
use lib "local/lib";
~~~
## %ENV
`%ENV` 包含当前环境的拷贝。该环境由 Perl 创建的子 Shell 所给予。
这对 `taint` 模式很重要,`%ENV` 具有能修改 Shell 行为的内容。正因如此, perlsec 推荐在 `taint` 模式执行命令时使用下列代码:
~~~
$ENV{'PATH'} = '/bin:/usr/bin'; # change to your real path
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
~~~
## %SIG
Perl 具有丰富的信号处理能力。使用 `%SIG` 变量,你能够在当信号发送给运行 的进程时执行任意子例程。
如果你有耗时进程,这将特别有用。通过发送信号(通常是 `SIGHUP`)来重载配置 ,你不必启动和停止进程。
通过分别赋值 `$SIG{__DIE__}` 和 `$SIG{__WARN__}`,你也可以更改 `die` 和 `warn` 的行为。
## <>
钻石操作符 `<>` 用于程序期望的输入时,而不用关心它如何到达。
如果程序收到任何参数,它们将分成文件名及其内容发送给 `<>`。否则,使用标准 输入(`STDIN`)。
`<>` 对于过滤程序特别有用。
## ``和__DATA__
如果程序包含自身为一行的魔法标记 `__DATA__`,那么它下面的任何东东均可通过 魔法`` 句柄为程序所用。
如果你想在程序中包含数据,但又想与主程序逻辑分开,那么这将特别有用。
## $!
当使用 `system` 执行命令时,如果命令返回非真状态,那么 `$!` 将为真。否则, 可能未被执行。`$!` 将包含出错消息。
## $@
如果使用 `eval`,那么 `$@` 将包含 `eval` 所抛出的语法错误。
';
对象、模块及包
最后更新于:2022-04-01 21:07:36
## 包基础
* 包是方法的集合
* 具有自己的命名空间
* 包方法能被导出或直接调用
~~~
Foo::bar()
Foo->bar()
bar() (如果 Foo 已导出它)
~~~
## 模块基础
* 模块是包含一个或多个包的文件
* 多数人交替使用模块和包
## 对象基础
* 对象是被 bless 的哈希引用(不必是哈希引用,但它最常见)
* bless 将单个类赋给对象
* 对象可被重新 bless
### 1;
* 模块必须以真值结束
* 不必是 1
* 包没有相同的限制
## @ISA
Perl 的对象继承方法使用 `@ISA` 来决定模块继承自什么类。多年前,通过直接 修改`@ISA` 声明继承。现在,多数程序使用 `base` 编译指令声明继承。
下列代码是等效的:
~~~
package Foo;
require Wango;
@ISA = ( "Wango" );
package Foo;
use base "Wango";
~~~
';
引用
最后更新于:2022-04-01 21:07:34
## 引用是引用其他变量的标量
引用像 C 中引用其他变量的指针。使用 `\` 操作符创建引用。
~~~
my $sref = \$scalar;
my $aref = \@array;
my $href = \%hash;
my $cref = \&subroutine;
~~~
引用指向的事物即其所指。
使用合适的印记解引用,首选使用花括号。
~~~
my $other_scalar = ${$sref};
my @other_array = @{$aref};
my %other_hash = %{$href};
&{$cref} # Call the referent.
~~~
## 用箭头符解引用更容易
要访问数组和哈希引用,使用 `->` 操作符。
~~~
my $stooge = $aref->[1];
my $stooge = $href->{Curly};
~~~
## ref vs. isa
* 一个引用属于一个类
* 你可以使用 `ref` 查检类
* 一个对象引用能从其他类继承
* 你可以使用 `isa` 来询问一个对象是否继承自一个类
* 没有好理由不要用 `ref`
* `isa` 是 _UNIVERSAL_ 包的一部分,因此你可以在对象上调用它
~~~
my $mech = WWW::Mechanize->new;
print "ok\n" if $mech->isa('LWP::UserAgent');
~~~
## 引用匿名子例程
子例程能被赋给变量,并被调用,以允许代码引用被传递及使用。这将十分有用, 比如编写需要执行所提供代码的子例程。
~~~
my $casefix = sub { return ucfirst lc $_[0] };
my $color = $casefix->("rED");
print "Color: $color\n"; # prints Red
~~~
';
构造
最后更新于:2022-04-01 21:07:32
## C 风格的循环通常不必要
你可以写 C 风格的循环,但常常不需要它们。
不要在 `foreach` 的位置使用它们:
~~~
for (my $i = 0; $i <= $#foo; $i++) { # BAD
foreach (@foo) { # BETTER
~~~
不要在 `while` 的位置使用它们:
~~~
for (my $i = ; $i; $i = ) { # BAD
while (my $i = ) { # BETTER
~~~
想想你编写的代码,并找找感觉。
## 匿名哈希和数组
创建一个匿名数组引用,并给它赋值:
~~~
my $array = [ 'one', 'two', 'three' ];
~~~
匿名是因为我们不必创建数组。
哈希有相似的构造器:
~~~
my $hash = { one => 1, two => 2, three => 3 };
~~~
看作你应认为的而非引用。
## `q[qrwx]?//`、`m//`、`s///` 及 `y///`
Perl 让你自行指定定界符:
* 单引号:`'text' => q/text/`
* 双引号:`"text" => qq/text/`
* 正则表达式:`qr/text/`。除此之外,在 Perl 匹配及替换操作符外没有别的方式指定正则表达式匹配。
* 单词:`("text", "text") => qw(text text);`
* 反引号:```text` => qx/text/``
* 正则匹配(`m//`)、正则替换(`s///`)、及转换(`tr///`、`y///`) 工作方式相同
你可以使用除空白之外的任意字符。但要注意平衡括号或花括号:
~~~
qq//
qq#A decent delimiter #
qq( man perl(1) for details ) # valid!
~~~
## `global`、`local`、`my` 及 `our`
* 使用 `use vars` 声明全局变量
* 使用 `my` 声明词法变量
* `local` 并非你所认为的,除非你知道为何使用 `local`,否则使用 `my` 代替
* 仅当你的包需要全局变量时使用 `our`
';
CPAN
最后更新于:2022-04-01 21:07:30
CPAN 是所有其他语言羡慕嫉妒恨的魔弹。它是人们贡献的巨大模块仓库。它意为_Comprehensive Perl Archive Network_。
## 在 search.cpan.org 搜索模块
[http://search.cpan.org](http://search.cpan.org/) 是 CPAN 搜索的标准界面。它也包括链向其他站点的 链接。
## 在 metacpan.org 替代搜索
CPAN 也有一个不同的界面 [http://metacpan.org](http://metacpan.org/)。metacpan.org 具有更多的 特性及用于浏览 CPAN 和发行的链接。
## 在 cpanratings.perl.org 查看关于模块的评论
[http://cpanratings.perl.org](http://cpanratings.perl.org/) 让你对模块进行打分及评论,在你试用之前, 不妨看看别人的看法。
## 在 rt.cpan.org 报告 Bug
从 [http://rt.cpan.org](http://rt.cpan.org/) 提交你的 Bug 到 RT。
## 在 annocpan.org 批注模块文档
[http://annocpan.org](http://annocpan.org/) 让你对模块文档进行批注,有望让作者在将来合并更改。
## 在 backpan.perl.org 查找模块的旧版本
[http://backpan.perl.org](http://backpan.perl.org/) 具有 CPAN 上的每个模块,甚至包含过时的旧版本。
';
外部程序
最后更新于:2022-04-01 21:07:27
在 Perl 中有三种方式来调用外部程序。
## `system()` 返回程序的退出状态
~~~
my $rc = system("/bin/cp $file1 $file2"); # returns exit status values
die "system() failed with status $rc" unless $rc == 0;
~~~
如果可能,用列表传递你的参数,而不是用单个的字符串。
~~~
my $rc = system("/bin/cp", $file1, $file2 );
~~~
如果 `$file1` 或 `$file2` 有空格或其他特殊字符,这将确保不会在 Shell 中出错。
`system()` 的输出不会被捕获。
## 反引号(\`\`)和 `qx()` 操作符返回程序的输出
当你想要输出时,使用:
~~~
my $output = `myprogram foo bar`;
~~~
你将需要检查 `$!` 中的错误代码。
如果你使用反引号或 `qx()`,首选 _IPC::Open2_ 或 _IPC::Open3_ 代替,因为 它们将给你相同的参数控制,并允许你捕获输出。
`IPC::Open3` 是在 Perl 中不使用 Shell 命令来捕获 `STDERR` 的仅有方法。
';
模块
最后更新于:2022-04-01 21:07:25
## 利用 `use lib` 在非标准位置搜索模块
要搜索没有安装到 `@INC` 所指定路径的模块,使用 `lib` 编译指令:
~~~
use lib '/home/andy/private-lib/';
use Magic::Foo;
~~~
注意:`use lib` 必须置于试图使用 `Magic::Foo` 之前。
## 利用 Module::Starter 创建新模块
[Module::Starter](https://metacpan.org/module/Module::Starter) 及其命令行工具 `module-starter` 创建模块发行套件的基本 框架,以便发布到 CPAN 上。它包含基本的代码布局、POD 指令、文档片断、基本 测试、`Makefile.PL` 和 `MANIFEST` 文件、以及 `README` 和 `Changes` 记录 的开头。
~~~
$ module-starter --module=Magic::Foo --module=Magic::Foo::Internals \
--author="Andy Lester" --email="andy@perl.org" --verbose
Created Magic-Foo
Created Magic-Foo/lib/Magic
Created Magic-Foo/lib/Magic/Foo.pm
Created Magic-Foo/lib/Magic/Foo
Created Magic-Foo/lib/Magic/Foo/Internals.pm
Created Magic-Foo/t
Created Magic-Foo/t/pod-coverage.t
Created Magic-Foo/t/pod.t
Created Magic-Foo/t/boilerplate.t
Created Magic-Foo/t/00-load.t
Created Magic-Foo/.cvsignore
Created Magic-Foo/Makefile.PL
Created Magic-Foo/Changes
Created Magic-Foo/README
Created Magic-Foo/MANIFEST
Created starter directories and files
~~~
## 利用 `h2xs` 创建 XS 模块
如果你想创建 XS 模块,即 Perl 代码与外部 C 代码的接口,那么你将需要使用 原始的模块开始工具 `h2xs`。`h2xs` 已包含到每个 Perl 发行中,但它可能并没 有`Module::Starter` 那么新。除非你需要 XS 代码,否则使用 `Module::Starter`。
## 利用 Dist::Zilla 创建、打包及发行模块
[Dist::Zilla](https://metacpan.org/module/Dist::Zilla) 是一个相当好用的 Perl 模块,它使创建、打包、以及发行 模块的过程变得十分容易。如果你打算将编写的模块发布到 CPAN 上,那么使用 Dist::Zilla 将为你节省许多时间。
### 初始化 Dist::Zilla 配置
安装 Dist::Zilla 之后的第一件事就是初始化其配置:
~~~
$ dzil setup
~~~
根据向导提供你的姓名、Email、选择版权许可、以及 PAUSE 帐号(发布模块 到 CPAN 时需要)即可。
Dist::Zilla 默认将配置文件保存在 `~/.dzil/config.ini` 文件中,所以后续 你也可以通过修改此文件来变更相应信息。
### 创建模块
执行以下命令可以创建一个新的模块,如 Foo::Bar:
~~~
$ dzil new Foo::Bar
[DZ] making target dir /home/xiaodong/code/Foo-Bar
[DZ] writing files to /home/xiaodong/code/Foo-Bar
[DZ] dist minted in ./Foo-Bar
~~~
这将创建如下目录结构及文件:
~~~
Foo-Bar
├── dist.ini
└── lib
└── Foo
└── Bar.pm
~~~
其中,`dist.ini` 为该模块的配置文件,`Bar.pm` 为模块源文件。
### 打包模块
待模块编写完毕,你就可以将模块打包了:
~~~
$ dzil build
[DZ] beginning to build WebService-TaobaoIP
[DZ] guessing dist's main_module is lib/WebService/TaobaoIP.pm
[DZ] extracting distribution abstract from lib/WebService/TaobaoIP.pm
[DZ] writing WebService-TaobaoIP in WebService-TaobaoIP-0.03
defined(@array) is deprecated at /usr/share/perl5/Log/Log4perl/Config.pm line
864.
(Maybe you should just omit the defined()?)
[DZ] building archive with Archive::Tar::Wrapper
[DZ] writing archive to WebService-TaobaoIP-0.03.tar.gz
~~~
执行该命令后,模块就会被打包成 `.tar.gz` 格式。
### 发布模块
如果你要将模块发布到 CPAN 上,只需执行:
~~~
$ dzil release
[DZ] beginning to build WebService-TaobaoIP
[DZ] guessing dist's main_module is lib/WebService/TaobaoIP.pm
[DZ] extracting distribution abstract from lib/WebService/TaobaoIP.pm
[DZ] writing WebService-TaobaoIP in WebService-TaobaoIP-0.03
defined(@array) is deprecated at /usr/share/perl5/Log/Log4perl/Config.pm line
864.
(Maybe you should just omit the defined()?)
[DZ] building archive with Archive::Tar::Wrapper
[DZ] writing archive to WebService-TaobaoIP-0.03.tar.gz
[@Basic/TestRelease] Extracting
/home/xiaodong/code/WebService-TaobaoIP/WebService-TaobaoIP-0.03.tar.gz
to .build/x8WYcGBWoY
Checking if your kit is complete...
Looks good
Writing Makefile for WebService::TaobaoIP
Writing MYMETA.yml and MYMETA.json
cp lib/WebService/TaobaoIP.pm blib/lib/WebService/TaobaoIP.pm
Manifying blib/man3/WebService::TaobaoIP.3pm
No tests defined for WebService::TaobaoIP extension.
[@Basic/TestRelease] all's well; removing .build/x8WYcGBWoY
*** Preparing to release WebService-TaobaoIP-0.03.tar.gz with
@Basic/UploadToCPAN ***
Do you want to continue the release process? [y/N]: N
~~~
根据提示,回答 `y` 将发布模块,`N` 将终止发布过程。
### 其他功能
Dist::Zilla 不愧为一站式工具,除上述基本功能之外,还包括添加模块到现有 发行、执行测试、列出模块依赖等特性。
有关 Dist::Zilla 的更多用法,可参考其[官方文档](http://dzil.org/tutorial/contents.html)。
';
调试
最后更新于:2022-04-01 21:07:23
## 开启 `strict` 和 `warnings`
无论何时调试代码,都确信已开启了 `strict` 和 `warnings` 编译指令。
将下面两行:
~~~
use strict;
use warnings;
~~~
放到你试图调试或将来可能想调试的程序的顶部。
[strict](http://perldoc.perl.org/strict.html) 编译指令强制你使用那些允许 Perl 在编译时找出错误的许多特性。 首先最重要的是,在 `strict` 下,变量必须在使用前声明。多数情况下,这意味 着使用 `my`:
~~~
use strict;
my $foo = 7; # OK, normal variable
print "foo is $fooo\n"; # Perl complains and aborts compilation
~~~
没有 `strict`,Perl 仍然会高兴地执行上面的程序。但你可能会感到杯具,想不 明白`$foo` 为何没有值。启用 `strict` 也会减少许多令人头痛的输入错误。
另外,`strict` 不允许使用多数裸字。
~~~
no strict;
$foo = Lorem;
print "$foo\n"; # Prints "Lorem"
use strict;
my $foo = ipsum; # Complains about bareword
$foo = (
Lorem => 'ipsum' # OK, barewords allowed on left of =>
);
$SIG{PIPE} = handler; # Complains
$SIG{PIPE} = \&handler; # OK
$SIG{PIPE} = "handler"; # Also, OK, but above is preferred
~~~
最后,如果你使用[符号引用](http://perldoc.perl.org/perlref.html#Symbolic-references),启用 `strict` 将抛出运行时错误。
~~~
no strict;
$name = "foo";
$$name = "bar"; # Sets the variable $foo to 1
print "$name $$name\n"; # Prints "foo bar"
use strict;
my $name = "foo";
$$name = "bar"; # Complains: can't use "foo" as ref
~~~
`warnings` 编译指令将使 Perl 吐出许多有用的抱怨,以便让你知道程序中的 某个东东并非你所想要的:
~~~
use warnings;
my $foo = ;
$foo += 3;
my $foo = 1; # Compains: redeclaration of variable
my $bar = '12fred34';
my $baz = $bar + 1; # Complains: Argument "12fred34" isn't numeric
# Complains: Name "main::baz" used only once
~~~
参阅 strict 及 warnings 的文档了解其他信息。关于不用 `strict` 所带来的 恐怖故事,可以看看 [PerlMonks](http://www.perlmonks.org/?node_id=482733) 上面的帖子。
## 检查每个 `open` 的返回值
你将经常看到人们抱怨下面的代码无法执行:
~~~
open( my $file, $filename );
while ( <$file> ) {
...
}
~~~
接着抱怨 `while` 循环也被破坏了。这儿的常见问题是文件 `$filename` 实际 上并不存在,因此 `open` 将失败。如果没有检查,那么你将从来不会知道。使 用以下代码替换它:
~~~
open( my $file, '<', $filename ) or die "Can't open $filename: $!";
~~~
## 利用 `diagnostics` 扩展警告
有时候警告消息并没有解释你想要的那么多。例如,为何你会获得此警告?
~~~
Use of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695.
~~~
试试将下列内容放到程序顶部并重新执行代码:
~~~
use diagnostics;
~~~
现在警告看起来像这样:
~~~
Use of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm
line 695 (#1)
(W uninitialized) An undefined value was used as if it were already
defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
To suppress this warning assign a defined value to your variables.
To help you figure out what was undefined, perl tells you what operation
you used the undefined value in. Note, however, that perl optimizes your
program and the operation displayed in the warning may not necessarily
appear literally in your program. For example, "that $foo" is
usually optimized into "that " . $foo, and the warning will refer to
the concatenation (.) operator, even though there is no . in your
program.
~~~
更多的信息将帮助你找出程序的问题。
记住你也可以从命令行指定模块和编译指令,因此你甚至不必编辑源代码来使用`diagnostics`。使用 `-M` 命令行选项再次执行它:
~~~
perl -Mdiagnostics mycode.pl
~~~
## 使用优化信号来获得栈跟踪信息
有时候你将获得警告,但你并不明白是如何得到的。比如:
~~~
Use of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695.
~~~
你可以看到模块的 695 行代码干了什么,但你无法看到你的代码在此时干了什么。 你将需要看到子例程被调用的跟踪信息。
当 Perl 调用 `die` 或 `warn` 时,它将分别指定 `$SIG{__DIE__}` 和 `$SIG{__WARN__}` 来通过子例程。如果你修改它们,让其成为比 `CORE::die` 和 `CORE::warn` 更有用的话,你就得到了一个有用的调试工具。这种情况,可以使用 `Carp` 模块的 `confess` 函数。
在你程序的顶部,添加这些行:
~~~
use Carp qw( confess );
$SIG{__DIE__} = \&confess;
$SIG{__WARN__} = \&confess;
~~~
现在,当代码调用 `warn` 或 `die` 时,`Carp::confess` 函数将处理它。`confess` 打印原始警告,跟着栈跟踪信息,然后停止执行程序。
~~~
Use of uninitialized value in string eq at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695.
at /Library/Perl/5.8.6/WWW/Mechanize.pm line 695
WWW::Mechanize::find_link('WWW::Mechanize=HASH(0x180e5bc)', 'n', 'undef') called at foo.pl line 17
main::go_find_link('http://www.cnn.com') called at foo.pl line 8
~~~
现在我们有更多信息来调试代码,包括精确的调用函数及传递的参数。从这儿, 我们能够容易地看到 `find_link` 的第三个参数是 `undef`,那将是一个开始 调查的好地方。
## 使用 Carp::Always 获得栈跟踪信息
如果你不想覆盖信号处理器,那么可以安装 CPAN 模块 [Carp::Always](https://metacpan.org/release/Carp-Always)。
在安装之后,添加下行到你的代码中:
~~~
use Carp::Always;
~~~
或者使用 `-MCarp::Always` 从命令行调用你的程序,这将总是会得到栈跟踪信息。
## 使用 Devel::REPL 交互执行 Perl 代码
[Devel::REPL](https://metacpan.org/release/Devel-REPL) 模块提供一个交互式的 Shell。通过该 Shell,你不用创建 临时的源代码文件就可以做快速的原型开发及测试代码。
在安装 Devel::REPL 之后,你可以执行以下命令启动 Shell:
~~~
$ re.pl
~~~
';
POD 格式
最后更新于:2022-04-01 21:07:21
## 内联文档及格式
POD 允许你在 Perl 代码中使用标记来创建文档。如果你见过 Javadoc 或 PHPdoc, 它跟它们很相像。
POD 是语言的一部分,并非额外的规范。
## 使用 `=head1` 和 `=head2` 创建标题
~~~
=head1 MOST IMPORTANT
Blah blah
=head2 Subheading
blah blah
=head2 Subhading
blah blah
~~~
## 创建无序列表
要创建这样的列表:
* Wango
* Tango
* Fandango
使用:
~~~
=over
=item * Wango
=item * Tango
=item * Fandango
=back
~~~
## 创建有序列表
要创建下面的列表:
1. Visit perl101.org
2. ???
3. Profit!
使用:
~~~
=over
=item 1 Visit perl101.org
=item 2 ???
=item 3 Profit!
=back
~~~
## 使用内联标记样式
POD 用 `B<>`、`I<>` 及 `C<>` 分别表示粗体、斜体、代码。
~~~
B
I
C
~~~
标记格式能够嵌套。
## 使用 `L<>` 超链接
`L<>` 既可以链接文档中的关键字,如 `L`;也可以链接 URL,如`L
';
子例程
最后更新于:2022-04-01 21:07:18
## 使用 `shift` 检索子例程的参数
子例程的参数来自于特殊的 `@_` 数组。不带参数的 `shift` 默认使用 `@_`。
~~~
sub volume {
my $height = shift;
my $width = shift;
my $depth = shift;
return $height * $width * $depth;
}
~~~
## 使用列表赋值来赋给子例程参数
你也可以使用列表赋值赋给子例程参数:
~~~
sub volume {
my ($height, $width, $depth) = @_;
return $height * $width * $depth;
}
~~~
## 通过访问 `@_` 直接处理子例程参数
在某些情况下,但我们希望很少,你能够通过 `@_` 数组直接访问参数。
~~~
sub volume {
return $_[0] * $_[1] * $_[2];
}
~~~
## 传递的参数能被修改
传递给子例程的参数是实际参数的别名。
~~~
my $foo = 3;
print incr1($foo) . "\n"; # prints 4
print "$foo\n"; # prints 3
sub incr1 {
return $_[0]+1;
}
~~~
如果你想要这种效果的话,这样更好:
~~~
sub incr2 {
return ++$_[0];
}
~~~
## 子例程没有检查参数
如果你喜欢,你能够将任意东东传递给子例程。
~~~
sub square {
my $number = shift;
return $number * $number;
}
my $n = square( 'Dog food', 14.5, 'Blah blah blah' );
~~~
该函数只会使用第一个参数。因为这个关系,你可以使用任意数目的参数, 甚至没有参数来调用函数。
~~~
my $n = square();
~~~
Perl 不会对此抱怨。
_Params::Validate_ 模块解决了许多验证问题。
## Perl 有原型,忽略它们
在演进的道路上加入了原型,因此你可以像这样干:
~~~
sub square($) {
...
}
my $n = square( 1, 2, 3 ); # run-time error
~~~
无论如何都不要使用它们。它们不会作用于对象,它们需要在调用子例程 前先予以声明。它们是好想法,但只是不实用。
## 利用 `BEGIN` 块在编译时做事
`BEGIN` 是一种特殊的代码块类型。它允许程序员在 Perl 的编译阶段执行 代码,这样可以执行初始化及做其他事情。
Perl 使用 `BEGIN` 在任意时导入模块。下列两个语句是等效的:
~~~
use WWW::Mechanize;
BEGIN {
require WWW::Mechanize;
import WWW::Mechanize;
}
~~~
## 传递数组及哈希引用
记住传给子例程的参数是作为一个大数组传递的。如果你像下面这样干:
~~~
my @stooges = qw( Moe Larry Curly );
my @sandwiches = qw( tuna ham-n-cheese PBJ );
lunch( @stooges, @sandwiches );
~~~
那么传给 `lunch` 的是列表:
~~~
( "Moe", "Larry", "Curly", "tuna", "ham-n-cheese", "PBJ" );
~~~
在 `lunch` 中,你如何能告诉 stooges 结束及 sandwiches 开始的位置? 你不能。如果你尝试这样:
~~~
sub lunch {
my (@stooges, @sandwiches) = @_;
~~~
那么所有 6 个元素都会跑到 `@stooges` 中,而 `@sandwiches` 什么都不会 得到。
答案是使用引用,正如:
~~~
lunch( \@stooges, \@sandwiches );
sub lunch {
my $stoogeref = shift;
my $sandwichref = shift;
my @stooges = @{$stoogeref};
my @sandwichref = @{$sandwichref};
...
}
~~~
';
文件
最后更新于:2022-04-01 21:07:16
## 利用 `open` 和 `<>` 操作符更易读取文件
使用 Perl 打开并读取文件很简单。下面的示例代码演示如何打开文件, 一行一行地读取,检查匹配正则表达式的文本,以及输出匹配的行。
~~~
open( my $fh, '<', $filename ) or die "Can't open $filename: $!";
while ( my $line = <$fh> ) {
if ( $line =~ /wanted text/ ) {
print $line;
}
}
close $fh;
~~~
总是检查 `open` 的返回码是否为真。如果为假,其结果在 `$!` 中。
## 利用 `chomp` 移除结尾的换行符
从文件读取行时会包含结尾的换行符。假如你有一个文本文件,其第一行是:
~~~
Aaron
~~~
`Aaron` 实际上是 6 个字符`Aaron\n`。此代码将失败:
~~~
my $line = <$fh>;
if ( $line eq 'Aaron' ) {
# won't reach here, because it's really "Aaron\n";
}
~~~
要移除 `\n` 及结尾的其他任意空白,调用 `chomp`。
~~~
my $line = <$fh>;
chomp $line;
~~~
现在 `$line` 为 5 个字符长。
## 利用 `$/` 更改行分隔符
可以更改输入记录分隔符 `$/`,其默认设置为 `\n`。
设置 `$/` 一次读取一段。设置 `$/` 为 `undef` 将一次读取整个文件。 参阅 [perlvar](http://perldoc.perl.org/perlvar.html) 了解细节。
## 一次读取整个文件
你将注意到新手在读取文件时会使用下述两种方法之一:
~~~
open (FILE,$filename) || die "Cannot open '$filename': $!";
undef $/;
my $file_as_string = ;
~~~
或:
~~~
open (FILE,$filename) || die "Cannot open '$filename': $!";
my $file_as_string = join '', ;
~~~
选择两种中的前者。第二种读取所有行到数组,然后组合成一个大字符串。 第一种仅读取到字符串,不会间接创建行列表。
然而最佳的方式是像这样:
~~~
my $file_as_string = do {
open( my $fh, $filename ) or die "Can't open $filename: $!";
local $/ = undef;
<$fh>;
};
~~~
`do` 块返回块中最后求解的值。此方法将 `$/` 设置为局部作用域,所以 超出块范围会设置为默认值。如果没有局部化 `$/`,那么它将保留设置的 值,其他代码段可能并不期望它被设置为 `undef`。
下面是另一种方法:
~~~
use File::Slurp qw( read_file );
my $file_as_string = read_file( $filename );
~~~
_File::Slurp_ 是一次性读取和写入的有用模块,它将在背后做魔术般的快速 处理。
## 利用 `glob()` 获取文件列表
使用标准的 Shell 展开模式来获取文件列表。
~~~
my @files = glob( "*" );
~~~
将它们传递给 `grep` 来做快速过滤。例如,要获取文件而非目录:
~~~
my @files = grep { -f } glob( "*" );
~~~
## 使用 `unlink` 移除文件
Perl 内置函数 `delete` 用来删除哈希的元素,而非文件系统中的文件。
~~~
my %stats;
$stats{filename} = 'foo.txt';
unlink $stats{filename}; # RIGHT: Removes "foo.txt" from the filesystem
delete $stats{filename}; # WRONG: Removes the "filename" element from %stats
~~~
术语 `unlink` 来自于 Unix 从目录节点移除文件链接的想法。
## 在 Windows 下使用 Unix 风格的目录
即使 Unix 使用 `/usr/local/bin`,而 Windows 使用 `C:\foo\bar\bat` 这样的路径, 你仍然能够在文件名中使用斜杠。
~~~
my $filename = 'C:/foo/bar/bat';
open( my $fh, '<', $filename ) or die "Can't open $filename: $!";
~~~
在这种情况下,Perl 在打开文件前魔术化地将 `C:/foo/bar/bat` 更改为`C:\foo\bar\bat`。 这也会防止文件名包含未引起的反斜杠所带来的问题。
~~~
my $filename = "C:\tmp";
~~~
`$filename` 包含 5 个字符:C、:、tab 字符、m、及 p。实际上,它应该写成:
~~~
my $filename = 'C:\tmp';
my $filename = "C:\\tmp";
~~~
或者你让 Perl 来照料它:
~~~
my $filename = 'C:/tmp';
~~~
';