《UNIX/Linux 系统管理技术手册(第四版)》——2.4 Perl编程-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

《UNIX/Linux 系统管理技术手册(第四版)》——2.4 Perl编程

简介:

本节书摘来自异步社区《UNIX/Linux 系统管理技术手册(第四版)》一书中的第2章,第2.4节,作者:【美】Evi Nemeth , Garth Snyder , Trent R.Hein , Ben Whaley著,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.4 Perl编程

UNIX/Linux 系统管理技术手册(第四版)
Larry Wall发明了Perl语言,它第一种真正伟大的脚本编程语言。它的能耐要比bash大得多,而且编写良好的Perl代码也相当容易阅读。另一方面,Perl没有给开发人员强加太多的风格规范,所以不考虑可读性的Perl代码显得很神秘。Perl也被诟病为只适合写(不适合读)的语言。

我们在这里介绍Perl 5,这个版本成为标准已经有10年了。Perl 6是一个仍处在开发之中的主要版本。参考perl6.org了解详情。

对于系统管理工作来说,Perl或者Python(2.5节开始讨论)都是比传统的编程语言(如C、C++、C#和Java)更好的选择。它们做得更多,但代码行数更少,程序员调试的痛苦更少,而且省却了编译的麻烦。

选择哪种语言通常取决于个人的偏好,或者取决于雇主强加给雇员的标准。Perl和Python都提供了由用户群编写的模块库和语言扩展库。Perl存在的年头更长,所以它提供的库长尾效应更为明显[译者注:即功能繁多影响力不大的库数量更庞大,累积起来的效应也更大]。不过,对于常见的系统管理任务而言,两者的支持库大致上等价。

Perl的口号是“条条大路通罗马。”要记住本节读到的大多数例子都能用别的方法来实现。

Perl的语句用分号分隔1。注释以一个井号(#)开头,并且一直到这一行的结尾。语句块用花括号括起来。下面是一个简单的“hello,world!”程序:

\#!/usr/bin/perl

print "Hello,  world!\n";

和bash程序一样,必须用chmod +x将这个文件改为可执行,或者直接调用Perl的解释程序执行它。

$ chmod +x  helloworld

$ ./helloworld

Hello, world!

Perl脚本中的代码行都不是shell命令;它们是Perl代码。bash可以让用户把一系列命令组合起来,把它叫做脚本,但Perl和bash不一样,除非让Perl去看外面的世界,否则Perl自己不会看。也就是说,Perl提供了许多和bash一样的惯例,如使用撇号来获得一条命令的输出结果。

2.4.1 变量和数组
Perl有3种基本数据类型:标量(也就是说,像数和字符串这样的一元量)、数组和哈希(hash)。哈希也叫做关联数组。变量的类型总是一目了然,因为它体现在变量名上:标量的变量以$开头,数组变量以@开头,而哈希变量以%开头。

在Perl语言里,“列表(list)”和“数组(array)”这两个术语经常混用,不过或许更准确的说法是,列表是一系列的值,而数组是能够保存这样一个列表的变量。数组里的各个元素都是标量,所以和普通的标量变量一样,它们的名字都以$开头。数组下标从零开始,数组@a里元素的最大下标是$#a。这个值加1就等于数组的长度。

数组@ARGV保存着该脚本的命令行参数。可以像访问其他任何数组那样来访问它。

下面的脚本展示了数组的用法:

#!/usr/bin/perl

@items  = ("socks",  "shoes", "shorts");

printf "There are %d  articles of  clothing.\n",  $#items + 1;

print "Put  on  ${items[2]}  first, then ", join("  and ", @items[0,1]), ".\n";

输出为:

$ perl  clothes

There are 3 articles of  clothing.

Put  on  shorts first, then socks and shoes.

仅仅在这几行代码中,看点就很多。冒着分散我们注意力的风险,我们在所举的每个Perl例程中都包含了几种常见的习惯用法。在每个例子之后的文字中,我们都会阐述其中的窍门。如果仔细阅读这些例子(不要胆怯,它们都不长!),在看完本章的内容之后,就会对Perl最常碰到的形式有了一些经验。

2.4.2 数组和字符串文字
在本例中,首先要注意(…)创建了一个列表。这个列表里的每个元素都是字符串,它们由逗号隔开。创建好这个列表之后,就把它赋值给变量@items。

Perl不严格要求所有的字符串都要用引号引起来。在这个具体的例子里,没有引号也能给@items赋初值。

@items = (socks, shoes, shorts);

Perl把这些没有用引号引起来的字符串称为“裸单词(bareword)”,它们就按上次访问的方式来解释。如果用任何其他方式解释都没有意义,Perl就尝试把它按一个字符串来解释。在有限的几种情况下,这样解释有意义,并且能让代码保持整洁。不过,这里不一定正好就是上述几种情况之一。即使自己能一直保持用引号把字符串引起来,也要有所准备,去分析别人写的没有用引号引起来的代码。

要初始化这个数组,更Perl化的办法是用qw(指quote words的缩写)操作符。它实际上是把字符串用引号引起来的一种形式,而且和Perl里大多数被引号引起来的实体一样,可以选择自己的限定符。下面的形式

@items  = qw(socks shoes shorts);

是最传统的办法,但是它容易误导人,因为qw之后的部分不再是一个列表了,它实际上是一个用空白分隔字符串形成的一个列表。下面的这个版本的写法

@items  = qw[socks shoes shorts];

也能用,而且对于真正要做什么来说,这种写法可能还更正确一点儿。注意逗号没了,因为qw已经包括了它们的功能。

2.4.3 函数调用
print和printf都能接受任意数量的参数,这些参数由逗号分隔。但是join(…)看上去更像是某种函数调用;它和print和printf有怎样的不同呢?

实际情况并非如此;print、printf和join都是普通的函数。如果不会引起歧义,Perl允许省略函数调用的圆括号,所以两种调用形式(带括号和不带括号)都很常用。在上面例子的print那句中,圆括号把join的参数同print的参数给区分开了。

我们可以说表达式@items[0,1]必定要按某种列表来算,因为它以@开头。这实际上是一个“数组段”或者叫子数组,0和1两个下标列出了在这个数组段里包含的数组元素的索引。Perl在这里也能接受一个范围值,就像其等价表达式@items[0..1]里出现的那样。这里也能接受单个数值下标:@items[0]是一个列表,里面只有一个标量,即字符串“socks”。在这种情况下,它等价于("socks")这个常量。

Arrays are automatically expanded in function calls, so in the expression

函数调用会自动扩展数组,如下面的表达式中

join("  and ", @items[0,1])

join接收到3个字符串参数:“and”、“socks”和“shoes”。它把第二个及后面的参数连起来,在两个参数之间插入第一个参数。结果是“socks and shoes”。

2.4.4 表达式里的类型转换
在printf那一行,$#items + 1计算得到数字3。由此看来,$#items是一个数值,但这并不是这个表达式要按数值计算的原因;"2" + 1也是可以的。关键在于运算符+,它总是暗指算术运算。它把自己的参数转为数字,并计算得到数值结果。类似地,点运算符(.)把两个字符串连接起来,它根据需要转换自己的操作数:"2" . (12 ** 2)得到“2144”。

2.4.5 字符串表达式和变量
和bash里的情况一样,双引号引起来的字符串可以进行变量扩展。和bash里一样的还有,如果必要,可以用花括号把变量名括起来,如${items[2]},从而避免歧义(这里的花括号只用于演示;它们不是必须的)。$暗示这个表达式应该按标量来算。@items是数组,但是它的任何一个元素都是标量,起名字的习惯就反映出了这一事实。

2.4.6 哈希
哈希(也称为关联数组)表示一组“键/值”对。可以把一个哈希当作下标(键)是任意标量值的数组;它们不一定是数字。但在实际中,数字和字符串都是常用的键。

哈希变量的第一个字符是%(例如,%myhash),但是和在数组中的情况一样,哈希里的单个值都是标量,所以也以$开头。哈希的下标用花括号而不是方括号括起来,例如$myhash{'ron'}。

哈希是系统管理员的一个重要工具。系统管理员编写的几乎每个脚本都会用到它们。在下面的代码中,我们会读取一个文件的内容,按照/etc/passwd里的规则分析它,然后用分析得到的若干项结果构造一个叫做%names_by_uid的哈希。这个哈希中每一项都是用户名和它对应的UID。

#!/usr/bin/perl while ($_ = <>) {

($name, $pw,  $uid, $gid,  $gecos, $path, $sh)  = split /:/;

$names_by_uid{$uid}  = $name;

}
%uids_by_name = reverse %names_by_uid;

print "\$names_by_uid{0}  is  $names_by_uid{0}\n";

print "\$uids_by_name{'root'}  is  $uids_by_name{'root'}\n";

和上面的那个示例脚本一样,我们在这几行代码里也加入了几点新思想。在开始介绍这几处细节之前,先看一下这个脚本的输出:

$ perl  hashexample /etc/passwd

$names_by_uid{0}  is  root

$uids_by_name{'root'}  is  0

while ($ = <>)这条语句一次一行读取输入,把它赋值给名为$的变量;和C语言一样,整个赋值语句的值是等号右边的值。当碰到输入的结尾时,<>返回一个出错值,然后结束循环。

Perl对<>的解释为,检查命令行看是否在那里给出了任何文件。如果提供了文件,那么它就依次打开每个文件,然后在循环里运行这个文件的内容。如果没有在命令行给出任何文件,那么Perl就考虑从标准输入获得输入。

在循环体内,把split返回的值赋值给一系列变量,split是个函数,它用传给它的正则表达式作为域分隔符,对输入字符串进行分隔。这里的正则表达式用斜线来界定;这只是另一种形式的引用符号,这种形式的引用专门用于正则表达式,但和双引号的解释很类似。我们同样也可以写为split ':'或者split ":"。

这个split要用冒号分割的字符串到底是什么,没有明确地指定。当split没有第二个参数的时候,Perl就认为要分割$_的值。说实话,即使这个匹配模式(即用冒号做分隔符)也是可有可无的;默认用空白做分隔符,但却会忽略开头的所有空白。

不过稍等一下,还要多说一点。甚至回到循环一开始的地方,给$_赋值的操作也是不必要的。如果简单地写

while (&lt;&gt;) {

那么Perl会自动把每行都保存在$里。用户不必明确地去访问保存输入行的变量,就可以处理这些行。把$用作默认操作数的做法很常见,只要在$或多或少有意义的地方,Perl都会允许用$。

在获得passwd文件内各个域值的多重赋值语句里

($name, $pw,  $uid, $gid,  $gecos, $path, $sh)  = split /:/;

等式左边出现的列表创建了split的“列表上下文”,告诉split返回结果是由所有的域构成的一个列表。如果是给一个标量赋值,例如,

$n_fields = split /:/;

split则运行在“标量上下文”里,只返回它找到的域的个数。通过使用wantarray函数,用户自己编写的函数也能区分标量上下文和列表上下文。该函数在列表上下文中返回一个真值,而在标量上下文中返回一个假值,在空(void)上下文里则返回一个不定值。

The line

下面这行代码

%uids_by_name = reverse %names_by_uid;

也有一些深意。列表上下文里的哈希(这里是reverse函数的一个参数)算成(key1, value1, key2, value2, …)形式的一个列表。reverse函数颠倒该列表的次序,得到(valueN, keyN, …, value1, key1)。最后,给哈希变量%uids_by_name赋值的时候把这个列表按(key1, value1, …)这样转换,因此得到了颠倒的索引。

2.4.7 引用和自动生成
虽然这两方面都是高级话题,但是如果我们不提一下它们的话,那就是我们的工作怠慢了。这里给出它们的简短总结。数组和哈希只保存标量,但用户经常想在其中再保存别的数组和哈希。例如,回到我们前面分析/etc/passwd文件的那个例子,用户可能想要把passwd文件里每一行所有的域都保存到一个用UID来索引的哈希结构里。

虽然不能直接保存数组和哈希,但是却可以保存对数组和哈希的引用,因为引用本身是标量。只要在变量名之前加上一个反斜线(例如,@array),或是用引用数组或引用哈希的语法,就可以建立对数组或者哈希的引用。例如,分析口令的循环就可以变成下面的样子:

while (&lt;&gt;) {

$array_ref = [ split /:/ ];

$passwd_by_uid{$array_ref-&gt;[2]} = $array_ref;

}

尖括号返回对一个数组的引用,这个数组里保存有分割后的结果。$array_ref->[2]的记法引用UID域,即$array_ref所指数组中的第三个成员。

这里不能用$array_ref[2],因为我们没有定义@array_ref这个数组;$array_ref和@array_ref是不同的变量。再进一步说,如果在这里错误地用了$array_ref[2],那么也不会得到出错消息,因为@array_ref是一个完全合法的数组名;只是不能给它赋值而已。

缺少报警消息似乎是个问题,但它可以说是Perl最好的特性之一,这个特性叫做“自动生成(autovivification)”。因为变量名和引用语法总是会清楚地表明要访问的数据结构,所以就不用手工随时创建任何数据结构了。只要进行最低可能层面的赋值操作,就会自动生成中间结构。例如,只用一次赋值操作,就可以创建一个指向数组的哈希结构,数组的内容又是指向哈希的引用。

2.4.8 Perl语言里的正则表达式
用=~操作符把字符串“绑定”到正则表达式上,就可以在Perl里使用正则表达式了。例如,下面这一行代码

if ($text =~  m/ab+c/) {

检查保存在$text里的字符串,看是否能够匹配正则表达式ab+c。要对默认字符串$_进行操作,只要省略掉变量名和绑定操作符即可。实际上,还可以省去m,因为默认就是执行匹配操作:

if (/ab+c/) {

替换操作也和匹配操作类似:

$text =~  s/etc\./and so  on/g;  # Substitute text in  $text, OR

s/etc\./and so  on/g; # Apply  to  $_

我们插入了一个g选项,用“and so on”替换所有出现的“etc.”,而不是只替换第一次出现的“etc.”。其他常见的选项有忽略大小写的i、用点号(.)匹配换行的s,还有m,即用^和$匹配各行行首和行尾,而不只是要搜索的文本的开头和结尾。

下面这个脚本演示了另外两个要点:

#!/usr/bin/perl

$names = "huey dewey louie";

$regex = '(\w+)\s+(\w+)\s+(\w+)';

if ($names =~  m/$regex/) {

print "1st  name is  $1.\n2nd name is  $2.\n3rd name is  $3.\n";

$names =~  s/$regex/\2 \1/;

print "New  names are \"${names}\".\n";

}  else {

print qq{"$names"  did  not match "$regex".\n};

}

该脚本的输出为:

$ perl  testregex

1st  name is  huey.

2nd  name is  dewey.

3rd  name is  louie.

New  names are "dewey  huey".

这个例子展示了由//括起来的变量怎样做扩展,这样一来,正则表达式就不必是一个固定的字符串了。qq是双引号操作符的另一种写法。

在执行一次匹配或者替换操作之后, $1、$2等变量的内容就和正则表达式里括号中捕获的内容相对应。这些变量的内容在做替换操作时也能用,此时用1、2等来引用它们。

2.4.9 输入和输出
打开一个文件执行读或者写操作时,就要定义一个“文件句柄”来标识这个通道。在下面的例子里,INFILE是/etc/passwd的文件句柄,而OUTFILE是关联到/tmp/passwd的文件句柄。while语句的循环条件是,它和我们已经见过的<>很相似,但它专指一个文件句柄。这条语句读取文件句柄INFILE中的每一行,直到文件结尾,这时while循环结束。每行的内容都放入$_变量。

#!/usr/bin/perl

open(INFILE, "&lt;/etc/passwd")  or  die  "Couldn’t open  /etc/passwd";

open(OUTFILE, "&gt;/tmp/passwd")  or die  "Couldn’t open  /tmp/passwd";

while (&lt;INFILE&gt;) {

($name, $pw,  $uid, $gid,  $gecos, $path, $sh)  = split /:/;

print OUTFILE "$uid\t$name\n";

}

如果成功打开了这个文件,则open返回一个真值,从而绕过对die子句的判断(让这个子句不必执行)。Perl的or操作符和||(Perl也有这个操作符)的作用很像,但是优先级更低。如果要特意先对操作符左边的全部内容做判断,然后Perl才关注失败的结果,那么采用操作符or一般是更好的选择。

Perl用来指定如何使用每个文件(读?写?追加?)的语法和shell一模一样。还可以用像"/bin/df |"这样的“文件名”来打开和shell命令联系的管道。

2.4.10 控制流程
下面的例子用Perl重新实现了一个bash脚本,我们早先用后者来验证命令行参数是否有效。读者可以参照2.2.3节里该脚本的bash版本。注意,Perl的if语句结构没有then这个关键字,也没有终结关键字,它只是一个用花括号括起来的语句块。

还可以在单个语句的后面追加if子句(或者unless子句,它是if子句的否定版本),使得该语句有条件地执行。

#!/usr/bin/perl

sub show_usage {

print shift, "\n"  if scalar(@_);

print "Usage:  $0  source_dir dest_dir\n";

exit  scalar(@_) ? shift : 1;

}

if (@ARGV != 2) {

show_usage;

}  else {  # There are two  arguments

($source_dir, $dest_dir) = @ARGV;

show_usage "Invalid  source directory" unless -d  $source_dir;

-d  $dest_dir or  show_usage "Invalid  destination directory";

}

在这个例子中,有两行代码使用了Perl的单目操作符-d,用来判断$source_dir和$dest_dir是否为目录。第二种形式(-d操作符在行首)有优势,它把实际的判断语句放在行首,这个位置最显眼。不过,用or来表示“否则”的意思有点儿难懂;有些读到这一代码的人可能会发现它容易让人误会。

按标量上下文(本例中由标量运算符来指出)取数组变量的值,返回的是该数组内元素的个数。这个值比$#array的值正好多1;在Perl里,有不止一种方法可以得到这个值。

Perl的函数从名为@_的数组里获得它们的参数。用shift操作符是访问这些参数最常用的方法,shift操作符删除参数数组里的第一个元素,并返回它的值。

这个版本的show_usage函数接受一则要打印的出错消息,但也可以不提供这个出错消息。如果提供了一则出错消息,那么还可以提供一个特殊的退出码。三目操作符?:计算其中第一个参数的值;如果值为真,那么返回结果就是第二个参数;否则就返回第三个参数。

和bash里一样,Perl也有一种专门的“else if”条件,但是它的关键字是elsif而不是elif(对于bash和Perl两种语言都用的人来说,这些有意思的小差别要么让人头脑聪颖,要么让人抓狂)。

如表2.5所示,Perl的比较运算符正好和bash的相反;字符串比较用文本运算符,而数值比较用传统的代数表达式。读者可以和2.2.7节的表2.2进行比较。
screenshot

在Perl里,可以使用表2.3中除-nt和-ot之外的所有文件测试操作符,-nt和-ot只有bash才支持。

和bash一样,Perl也有两种类型的循环。比较常见的循环形式是通过一个明确的参数列表。例如,下面的代码通过一个动物的列表来循环,每行打印一个动物的名字。

@animals = qw(lions tigers bears);

foreach $animal (@animals) {

print "$animal \n"  ;

}
也可以采用更传统的C风格的循环:

for  ($counter=1; $counter &lt;= 10;  $counter++) {

printf "$counter ";

}

我们给出了传统的for和foreach两种写法,但实际上,它们两个在Perl里是相同的关键字,可以根据自己的偏好选用其中任何一个。

在Perl 5.10(2007年)版之前都没有明确的case或者switch语句,但可以用几种办法取得相同的效果。用多层嵌套的if语句显然是一种办法,但除了这种不太好的做法之外,另一种可能的方法是用一条for语句设置$_的值,然后提供一种上下文,让last语句可以从中跳出来:

for  ($ARGV[0]) {

m/^websphere/  &amp;&amp; do  {  print "Install for  websphere\n"; last;  }; m/^tomcat/ &amp;&amp; do  {  print "Install for  tomcat\n" ; last; }; m/^geronimo/  &amp;&amp; do  {  print "Install  for geronimo\n";  last;  };

print "Invalid  option supplied.\n"; exit 1;

}

用多个正则表达式和$_里保存的参数进行比较。匹配不成功的话,就绕过&&并直接落入下一个测试条件。只要匹配了一个正则表达式,那么就执行相应的do语句块。然后由last语句从for语句块里立即跳出。

2.4.11 接受和确认输入
下面的脚本把我们在前面碰到的许多Perl结构都结合了起来,包括例程(函数)、后缀形式的if语句,以及for循环。这个程序本身只是主函数get_string的一个封装程序,get_string是一个检查输入有效性的通用例程。这个例程提示输入一个字符串,删除结尾的所有换行符,然后核对该字符串是否为空。空字符串会提示重新输入,三次之后脚本就放弃尝试。

#!/usr/bin/perl

$maxatt = 3; # Maximum tries to  supply valid input sub get_string {

my  ($prompt, $response) = shift;

# Try  to  read input up  to  $maxatt times

for  (my  $attempts = 0; $attempts &lt; $maxatt; $attempts++) {

print  "Please try  again.\n" if $attempts;

print "$prompt:  ";

$response = readline(*STDIN);

chomp($response);

return $response if $response;

}

die  "Too  many failed input attempts";

}

# Get names  with get_string and convert to uppercase

$fname = uc  get_string "First  name";

$lname = uc get_string "Last  name";

printf "Whole name:  $fname $lname\n";

这个脚本的输出为:

$ perl  validate

First  name: John  Ball

Last name: Park

Whole name: JOHN BALL PARK

在函数get_string和for循环中,都用my操作符创建有局部作用域的变量。在默认情况下,Perl中的变量都是全局变量。

对get_string里的局部变量列表进行初始化的时候,只从该函数的参数数组中获得了一个标量。对于初始化列表中的变量,如果没有获得相应的值(本例中是$response),那么就保持未定义的状态。

传给函数readline的*STDIN是一个“类型通配(typeglob)”,这在语言设计上是一个让人讨厌的缺点。最好不要太深入地去搞清楚它的确切含义,以免搞得自己头大。简单解释说,就是Perl的文件句柄不是一流的数据类型,所以一般必须在它们的名字之前加一个星号,才能当做参数传给函数。

在给$fname和$lname赋值的语句里,uc(代表“convert to uppercase”,即转为大写)和get_string两个函数调用都不带括号。因为在一条语句里不可能出现混淆,所以这样写没问题。

2.4.12 Perl用作过滤器
不通过脚本也能用Perl,只要在命令行写单独的表达式就行。这是做快速文本转换的一种好方法,这种方法几乎取代了比较老的过滤器程序,如sed、awk和tr。

用命令行选项-pe可以对STDIN循环处理,对每行做一次简单匹配,然后打印结果。例如,下面这条命令

ubuntu$ perl  -pe  's#/bin/sh$#/bin/bash#' /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/bash

…

把/etc/passwd里每行末尾的/bin/sh替换为/bin/bash,然后把转换后的passwd文件送到STDOUT。看到用斜线作为定界符(例如,s/foo/bar/),配合文本替换操作符一起使用,这种形式对读者来说可能更习惯,但是Perl允许用任何字符。在这里,搜索文本和替换文本都带有反斜线,所以用#作为定界符更简单。如果用成对的定界符,那么必须用4个,而不是通常的3个,例如,s(foo)(bar)。

Perl的-a选项打开了自动分隔模式,这种模式把输入行分成若干个域,保存在名为@F的数组里。默认的域分隔符是空白,但可以用-F选项设置另一种分隔符模式。

自动分隔模式和-p或者-n(-p的变体,它表示不自动打印)配合使用很方便。例如,下面的前两条命令用perl -ane把df两种不同形式的输出进行分隔。第三行命令接着执行join操作,把两组结果按Filesystem域拼接到一起,生成的组合表里包括两个df输出版本中的所有域。

suse$ df  -h  | perl  -ane 'print  join("\t",  @F[0..4]), "\n"'  &gt; tmp1 suse$ df  -i  | perl  -ane 'print  join("\t",  @F[0,1,4]),  "\n"'  &gt; tmp2 suse$ join  tmp1 tmp2

不用临时文件的脚本如下:

#!/usr/bin/perl

for  (split(/\n/, ‘df  -h‘))  {

@F = split;

$h_part{$F[0]}  = [ @F[0..4] ];

}

for  (split(/\n/, ‘df  -i‘) {

@F = split;

print join(“\t”, @{$h_part{$F[0]}}, $F[1], $F[4]), “\n”;

}

真正厉害的地方在于,可以把-i和-pe连起来用,就地编辑文件;Perl把文件读进来,提供文件里要编辑的那些行,然后再把结果保存回原来的文件。可以给-i提供一个模式,告诉Perl怎样备份每个文件原来的版本。例如,-i.bak把passwd文件备份为passwd.bak。要小心——如果没有提供一个备份模式,那么就根本得不到备份了。注意,在-i和后缀名之间没有空格。

2.4.13 Perl的附加模块
位于cpan.org的CPAN(Comprehensive Perl Archive Network,综合Perl存档网络)是一个Perl语言的大宝库,里面有用户贡献的第三方库。用cpan命令安装新的模块非常简单,就像把yum或者APT这样的包管理软件用到Perl模块上一样。如果在Linux系统上,那么检查一下,看该Linux发行版本是否把要找的模块已经放在软件包里,成为一个标准的功能——在系统层面只安装一次,然后让系统以后负责该模块的升级,这种做法更简单。

在没有cpan命令的系统上,可以运行perl -MCPAN -e shell,从另一条途径达到同样的效果:

$ sudo perl  -MCPAN  -e  shell

cpan shell --  CPAN exploration and modules installation (v1.9205)

ReadLine support available (maybe install Bundle::CPAN or  Bundle::CPANxxl?)

cpan[1]&gt; install  Class::Date

CPAN: Storable loaded ok  (v2.18)

CPAN: LWP::UserAgent  loaded ok  (v5.819) CPAN: Time::HiRes loaded ok  (v1.9711)

… several  more  pages  of status updates  …

用户还可以把Perl模块安装到自己的主目录里,供个人使用,但是这个过程并不简单。我们推荐采用一种自由的策略,把CPAN的第三方模块安装到系统层面上;Perl社区提供了一个发布模块的中心点,放开代码供人们检查,贡献模块的人都要给出自己的名字。Perl模块不会比任何别的开源代码更危险。

为了获得更好的性能,许多Perl模块都用到了C写的组件。安装这样的模块就会涉及去编译这些组件,所以需要一个完整的开发环境,其中要包括C编译器和一整套库。

和大多数语言一样,Perl程序里最常见的错误是,重复实现已经由社区编写的模块所提供的功能2。养成习惯,在解决任何Perl的问题时首先访问CPAN,这样做会节省开发和调试的时间。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: