![]() |
跟我学Perl(一) |
如果你曾经使用过linux,无论时间长短,都必定听说过perl;甚至可能在不知道的 情况下运行了不少perl的脚本。很多服务程序,象“inews”、“mirror”、“debconf ”、“majordomo”、“sirc”等等,都是纯粹用perl写的。在Debian区域的“Package s.gz”文件里做一个简单的的“zgrep”,你就可以知道有382个包需要perl的支持(也 就是意味着这些包都至少有一部分是用perl写的。),其它28个软件包也建议或推荐使 用perl支持。 但是Perl语言有什么出色之处呢? "Perl语言在文本处理方面非常突出,它把不同的内容联成一个整体。对于这种脚本语 言来说所有的那些不同的元素,看起来都是一样的。” ——John Ousterhout,Tcl脚本语言的作者 Perl即“Practical Extraction and Report Language”(实用析取报表语言)。 是的,比较粗,但是我想那就是你所得到的——如果你打算确信$HUNN NGOUS CORP并且 使用它的话。事实上,Larry Uall< Larry@wall org > (Perl的创立者)在Perl的帮助手 册页里提出:“Perl实际上代表的是Pathologically Edectic Rubbish Lister,但不要 告诉任何人我这样说过。”唔,他是Larry,对此我又能再说些什么呢? Perl已经被广泛的认为是“一种拥有各种语言功能的梦幻脚本语言”、“Unix 中的 王牌工具”。以及其他的一些类似的称呼,这些都是赞誉之辞,Perl被用来写单行脚本 ,快速执行程序,大的规划项目(Amazon.com的所有评论产品和控制系统,Netscape的 内容策划管理和传送系统,人类整组基因工程的DNA排序以及计划管理等等)。还有数以 百万计的令我们惊讶的各种各样的事情的高速程序。Perl还能够实现许多UNIX的公共系 统工具的功能(提示:如果你正在学或必须学awk, sed, grep和 tr,我建议你以Perl来 代替它们。所有的功能,更快的执行效率,相信你将永远不会因为它的性能发展太快而 不适应它。) 就像你对所有的现代语言所期望的那样,Perl允许你建立面向对象的程序。它也可 以进行网络操作(例如socket等等),并且有良好的可移植性(一个写得好的脚本可以 在Linux,BSD,Solaries,Dos,Win9x,NT,MacOS,OS/2, AnugaOS,VMS等操作系统 中不需要任何修改的运行),编写和调试周期很短——由于没有编译的要求,你只需将 变化的部分写出,就可以运行脚本。还有数目庞大的可适用于执行任何一项任务的模块 (即预建立Perl的例程),Comprehensive Perl Archiue Network (CPAN)就是每一个P erl程序员所能拥有的最好的模块库之一。 哦,但那是真的吗? 问得好,我希望在你使用Perl大约一年之后,就能告诉我答案。一样东西的描述就 像一个容器……,我仍旧在试图寻找一个适合Perl的足够大的容器(要是带有能锁的盖 子就更好了)。 那Perl不适用的方面呢? 我不会用Perl去写一个GUI文字处理程序,一个图形游戏,或者一个图形浏览器。P erl能通过与许多其他语言的接口真正的交互,所以你可以实现刚才所说的全部程序。但 在我看来,在其它编程语言里有许多更有效的方法去做那些事情。“对一个拿着锤子的 人来说,所有的问题看上去都像是钉子”——程序员们小心了。 值得注意的是,Perl本身并不是用Perl写成的,也不是Linalx的内核。那些底层的事务 用C/C++来处理会更好的。“用最适合的工具去做最适合的工作”应成为每个程序员的座 佑铭。 在拉开跳伞索之前的最后一次告诫。 如果你对Perl有一点了解,或者看过《Aint The Way I Learned It》系列内容的话 ,一定记得Perl的口号:“做事总有不止一种办法(There's More Than One Way To D o It)”。这常被缩写为TMTOMDI,并称为“tim-today”,是Perl的核心观点之一。当 然,任何对明显的错误的订正都非常欢迎。 那些看过我早些时间写的关于Shell脚本的丛书的人也许记得,一个脚本开始都有被 称作hash-bang或shebang的行: #!/bin/ bash 这行告诉外壳(shell)派生一个subshell(子shell),随后的代码将被这个特定 的subshell程序解释执行。Perl脚本也一样,第一行必须是: #!/usr/bin/Perl 或者任何一个正确的Perl解释器的路径。 注意到Hash-bang的必要条件: 1) 它必须是在脚本中的第一行。 2) #号必须是本行的第一个字母,并且在#号和!号之间不能有任何字母或符号。 3) 必须使用绝对的路径,而不是只有可执行程序的名称。 下面,让我们试着写出我们的第一个 Perl脚本: #!/ usr/bin/Perl # "goodbye" - a modern, high-angst replacement for "Hello World" print "Goodbye, cruel world! "; unlink $0; 嗯,至少在离开以前说了“再见”;礼貌夫人都会为此骄傲的。我们在这个脚本里做 了什么呢?有几件事情是相当明显的:第一,“hash-bang”,第二,一行告诉我们脚本 要做什么的说明—从shell脚本发展出来的另一个东东,并且这是个不错的想法(在代码 中并没有过多的解释!)。第三,我们用“print”函数打印出所要的信息。注意,在一 串字符末尾的“ ”:Perl不会自动为你提供一个换行,所以你必须自己决定要还是不要。还要注意,在 每一行代码末尾的分号:就像C语言,Perl也有同样的要求,不幸的是这常被编程者所忘 记。实际上,由于有着相对易懂的信息,Perl的错误检查十分容易,作为代码语句的分 隔标志,分号常被认为是下一行的前导符。如果你注意到这个问题,并不麻烦,最好记 住使用分号。 最后一行的意思是删除 “goodbye cruel world”文件,符号“$0”仅仅是被运行 堵塞的脚本的名字的引用,“unlink”做了和“rm”一样的事。注意,“$0”比“good bye”甚至是“/goodbye”有用的多,不管文件是什么名字,“$0”将返回这个文件名。 顺便说说一些指示代码 写完美无缺的代码对我来说有一些不切实际。在过去的一些场合,我写了些“writ e-only”代码,这将使任何试着读它的变成不同的颜色。我经常尝试着提高水平,我真 的希望看到这个想法得以实现。 空白——tab和space键——在Perl中受到了忽略,也就是说,它是无所谓有无所谓 无的。正因为这一点,你能格式化你的Perl代码来表达你正想要表达的思想,举个比较 简单的例子; @boats=(“Aloa”,“Cheoy Lee”,“Pearson”,“Mason”,Swem,“Westsail”,“S2 ”,“Petersen”, “Hereshoff”);#海船的名单列表。 这个例子中,我们用一些海船的名字填入数组“@boats”中,下面的例子也许更加 容易理解: @array=(“Aloa”,#法国OSTAR/IOR的船 “Cheoy Lee”,#舒适但是昂贵 “Pearson”,#结实但是笨重 “Mason”,#设计良好,但有些雍肿 “Swan”,#上等船只,如果你是大款 “Westsail”,Westsail在双尾船里算是相当不错了 “S2”,#漂亮的海湾船只——但不适宜远洋 “Petersen”, #钢铁巡洋舰,宽敞但援慢 “Hereshott”,#快速而华丽,狭窄而昂贵 这个习惯不仅仅在Perl中得到应用。绝大多数的现代语言允许附加空白,这样可以 使代码更加易懂。当我写这个系列文章的时候,我将尽最大努力示范至少我自己认为好 的代码样式的版本。同时也希望每一个人在创建他们自己的代码时认真考虑这件事。 变量 在Perl里,易用性非常重要的。它是一种被称作“格式宽松”的语言,其中变量的 定义并没有严格要求。实际上,没有什么方法可以定义32位的浮点数变量。 Perl中有三种变量,它们分别是标量型变量(Scalars),数组型变量(arranys)和散 列表(hashes),除了这些颇令人头疼的名字,他们其实都很简单:仅仅包含一些数据 的不同排列而已。 标量型变量——数字,字符串和引用 一个标量型变量由$标志来表示,例如$nuru,$joe,$pointer。 例: “0.0421”,“Joe’s gloue”,内存地址“0Xa000” 数组型变量——联系数字标识的标量型变量的列表或者说集合 一个数组型变量由@标志来表示,例如@v,@list,@variable 例: 0-----“Sundary” 1-----“Mondeny” 2-----“Tuesday” 3-----“Wednesday” 散列表——标量型列表的引用键 一个散列表变量由%标志来表示,例如%people,%x,%this_is_a_hash。 例: ridcnt ——“Sherlock Holmes” addr——“221B Backer Street” code—“NW1” city—“Londen” country—“Steuth” …… 注意,虽然数组型变量是按数字顺序存储的,但散列表却不是——恢复散列表的第 一个元素的往往和你载入的第一个元素毫无关联。散列的元素通过文本键代替它们在结 构中的位置来进行索引。 利用这三种数据类型,你可以表示你想要的任何事,并且会很轻松的实现。 另外很重要的一点:$a,@a 和%a三者之间完全毫无关联,他们处在不同的名字空间 。我在设计程序时,尽量小心不使用这些在视觉容易产生混淆的名字。特别是像$a[0]( 数组@a的第一个元素的引用)这种变量已经存在的情况下——这的确是你需要注意的。 可以给变量赋不同类型的值——数字型和字符串——我们下一步将进行有关两种类 型的共同的操作。Perl为你提供这些,但应该记住哪些类型的用哪些操作符。 Operator Num Str 等于 == eql 不等于 != ne 小于 < Lt 大于 > gt 小于等于 <= le 大于等于 > = ge 当通过比较这些字母或字符串之后会觉得他们异常容易记忆和使用——比较字符的 时候,就使用字符。 以前我总是给出具体的例子,但这次给你们出一道足以使你们头发变白,身心疲惫 的难题: #!/usr/bin/perl # A Political evaluation script $a=“A1” $b=“George”; if($a> $b)print $a wordinake a better Presideut If($a<$b) print $b word make a betler Presiclent ; If($a=$b)Print $a or $b, tlere’s no Diflereue ; 输出结果表明其中并没有什么不同,这也许只反映出一个政治性的现实,但我们比 较出的结果又是什么呢?对了,我们应该使用字符串操作,不对吗? #!/usr/bin/perl # A political evaluation script $a = "Al"; $b = "George"; if ( $a > $b) { print "$a would make a better President. "; } if ( $a < $b) { print "$b would make a better President. "; } if ( $a==$b) { print "$a or $b, there's no difference... "; } 呵呵,输出表明它们俩没有什么不同的地方。这也许是政治上的真实反映,但对我 们期望的比较输出却……哦,对了,我们应该使用字符串操作符,恩? #!/usr/bin/perl # A political evaluation script $a = "Al"; $b = "George"; if ( $a gt $b) { print "$a would make a better President. "; } if ( $a lt $b) { print "$b would make a better President. "; } if ( $a eq $b) { print "$a or $b, there's no difference... "; } 现在,比较操作符才能正确的工作。(现实世界的逻辑却并非如此……我离题了。 ) 顺便提一下,在第一个例子中为什么Perl会认为“Al”和“George”是等值的呢? 什么时候程序中参进了政治观点? 很重要的一点原因是,它要依赖Perl分辨“真(true)”与“假(false)”的方法 。可以进行一些试验,如“if”,“while”,“wntil”等,根据实验的结果,我们可 以理解这一点。 “0”意味着false,不管它是个数字还是字符串。 所有没有定义的变量(那些没有赋于任何值的量)都是false。 一个空的字符串——“”或‘’——也是false。 其余的都是true。 好了,这儿有一些较麻烦的例子,看看这些量,判断他们是true还是false: “00” “-1” “ ” “5-5” 请在这篇文章的注1中寻找答案,这里先卖一个关子。 另外一个比较重要的问题是变量的输出,这是决定引号中的变量是否需要解释说明 的方法,举例如下: $name = ‘Bessie’; print ‘Our cow is named $name。’; 输出为: Our cow is named $name。 我并不认为如果那样叫的话,会有自我意识强烈的母牛到来(我已不打算再提发音 的困难)。但是,我们怎样才能使Bessie显示出来呢? #注意过去是单引号而现在是双引号的地方。 $name = ‘Bessie’; print “Our cow is named $name。”; 通过Perl达到了我们的目的,我说过你可以做任何事情。 如果我们想打印变量的名字该怎么做呢?对Perl来说那太简单了: $joe = “Joe”; print “The variable $joe contains the value $joe。”; 我们可以打印出任何转义字符——也就是说字符在 Perl中有特殊的含义——但需在 他们前面加一个反斜杠。看看下面的例子: $joe = "Joe"; print "The variable "$joe" contains the value "$joe.""; 唔…… TMTOWDI: print 'The variable "$joe" contains the value "', $joe, '".'; 按照你自己的选择,一定要理解他们之间的不同之处,注意在“print”语句中使用 逗点作为分隔符的重要性,没有这个逗点,语义将截然不同,这一点我们将在以后的文 章中讨论。 在结束这篇文章之前,还有一点应重点考虑,为创建脚本文件时,通常使用“-W” 参数作为hash-bang的一部分: #!/usr/bin/perl -w 这样会对脚本中的问题发出警告,并指出这些问题的所在,如果你是一个Perl的初 学者一定要记住这个用法,但如果你是一个Perl的专家那就更会记住使用这个开关。那 些错误不会随着你的进步而消失,他们会很兴奋的增多:)。 尾声 这段时间,我们进行了一段短途旅行,伴随着一段轻快的爵士乐,我们轻盈的跳过 巨岩和戈滩,下个月我们将向更深的层次进展;也许研究数组或散列表,也许向拥有不 可思议的强大功能的“常规表达”或Perl的regexes发起冲刺,此时,我建议尝试一些我 们已经讨论过的东西,或许自己做一些实践,我发现了最好的学习语言的方法,勇往直 前直到极限,然后把我所遇到的困难向比我了解的更多的人请教,如果你不知道问题的 话你就不会得到理想的答案。 各位Perl之旅愉快。 perl -we '$@="145143150157404211216516316440141156". "1571641501451624012014516215440110141143153145162". "424076405714414516657164164171";`$@`' 注释[1];全是true,他们都不符合“假”的范围:“00”与“0”不同;“-1”也与“ 0”不同,空格“ ”,并不是空的字符串( ),“5-5”,没有核定,并不是“0”,参 考书:“Perl:The Complete Reference”,Martin C. Brown 有关Perl的专有名词 Perl——纵览 Perltoc——TOC文档 PerlSYN ——语法 Perlrun——执行 Perltrap——容易犯错之处 Perlfaq——常见问题 Perldate——数据结构 Perlop——操作和优先权 Perlfunc——内建函数 Perlkstyle——格式向导 Perldoc 和 Perldoc-f' |