最近在研究 openSUSE 的 OBS (編譯系統(tǒng)),其中很多部分用到 Perl,。而且自己也感到有必要學(xué)點(diǎn) Perl,,當(dāng)有一點(diǎn)其他程序語(yǔ)言的基礎(chǔ),,再學(xué)一門(mén)語(yǔ)言,入門(mén)還是非常簡(jiǎn)單的,。Perl 在 GNU/Linux 上應(yīng)用如此廣泛,,很多地方替換 shell 腳本會(huì)更靈活優(yōu)秀高效,學(xué)習(xí)它也很有必要,。本文是學(xué)習(xí)時(shí)的筆記,,多是語(yǔ)法使用示例,,沒(méi)有什么說(shuō)明,。擬研究 OBS 時(shí),依據(jù)某個(gè)應(yīng)用再寫(xiě)一篇 Perl 應(yīng)用教程,。
標(biāo)量
標(biāo)量是 Perl 中最簡(jiǎn)單的數(shù)據(jù)類(lèi)型,。大多數(shù)的標(biāo)量是數(shù)字(如 255 或 3.25e20)或者字符串(如 hello或者蓋茨堡地址)。
數(shù)字
perl中所有數(shù)字內(nèi)部的格式都是雙精度浮點(diǎn)數(shù),。
浮點(diǎn)數(shù)
1.25 255.000 255.0 7.25e45 #7.25x10 的 45 次方(一個(gè)大整數(shù)) -6.5e24 # -6.5x10 的 24 次方(一個(gè)大的負(fù)數(shù)) -12e-24 #- -12x10 的-24 次方(很小的負(fù)數(shù)) -1.2E-23 #指數(shù)符號(hào)可以大寫(xiě)(E)
整數(shù)
0 2001 -40 255 61298040283768
其中 61298040283768 也可以寫(xiě)作:
61_298_040_283_768
非十進(jìn)制整數(shù)
0377 #八進(jìn)制數(shù)字 377,等同于十進(jìn)制數(shù)字 255 0xff #十六進(jìn)制數(shù)字 FF,等同于十進(jìn)制數(shù)字 255 0b11111111 #等同于十進(jìn)制數(shù)字 255
可以用下劃線(xiàn)表示:
0x1377_0B77 0x50_65_72_7C
數(shù)字操作符
2+3 #2+3,5 5.1-2.4 #5.1-2.4,2.7 3*12 #3*12,36 14/2 #14/2,7 10.2/0.3 #10.2/0.3,34 10/3 #通常是浮點(diǎn)除,3.33333... ...
字符串
單引號(hào)字符串
'fred' #四個(gè)字符:f,r,e,d '' #空字符(沒(méi)有字符) 'hello\n' '\'\\' #單引號(hào)(')跟著反斜線(xiàn)(\)字符串
單引號(hào)中的 "\n" 不會(huì)被當(dāng)作換行符來(lái)處理,。
雙引號(hào)字符串
"barney" #等同于 'barney' "hello world\n" #hello world,換行
字符串操作符
鏈接操作符 "."
"hello"."world" # 同于 "helloworld" "hello".''."world" # 同于 "hello world" 'hello world'."\n" # 同于 "hello world\n"
重復(fù)操作符 "x"
"fred" x 3 # "fredfredfred" 5 x 4 # 等于 "5" x 4, "5555"
數(shù)字和字符串之間的自動(dòng)轉(zhuǎn)換
大多數(shù)情況下,Perl 將在需要的時(shí)候自動(dòng)在數(shù)字和字符串之間轉(zhuǎn)換。它怎樣知道什么時(shí)候需要字符串,什么時(shí)候需要數(shù)字呢?這完全依賴(lài)于標(biāo)量值之間的的操作符,。如果操作符(如+)需要數(shù)字,Perl 將把操作數(shù)當(dāng)作數(shù)字看待,。如果操作符需要字符串(如 . ), Perl 將把操作數(shù)當(dāng)作字符串看待。不必?fù)?dān)心數(shù)字和字符串的區(qū)別;使用恰當(dāng)?shù)牟僮鞣?Perl 將為你做剩下的事,。
"12" * "3" # * 操作符需要數(shù)字,所以結(jié)果為 36 "12fred34" * " 3" # 結(jié)果仍然是 36 , 后面的非數(shù)字部分和前面的空格都過(guò)濾掉,。
"Z" . 5 * 7 # 等于 "Z".35,, 或 "Z35"
Perl 內(nèi)嵌的警告
使用 -w 參數(shù)可以打開(kāi)警告:
$ perl -w perl程序 # 命令行執(zhí)行警告 #!/usr/bin/perl -w # 源代碼中使用警告
標(biāo)量變量
標(biāo)量變量可以存放一個(gè)標(biāo)量值。標(biāo)量變量的名字由一個(gè)美圓符號(hào)($)后接 Perl 標(biāo)識(shí)符:由字母或下劃線(xiàn)開(kāi)頭,后接字母,數(shù)字,或者下劃線(xiàn),?;蛘哒f(shuō)由字母,數(shù)字和下劃線(xiàn)組成,但不能由數(shù)字開(kāi)頭。大小寫(xiě)是嚴(yán)格區(qū)分的:變量$Fred 和變量$fred是不同的,。任意字母,數(shù)字,下劃線(xiàn)都有意義,如:
$a_very_long_variable_that_ends_in_1 $a_very_long_variable_that_ends_in_2
標(biāo)量賦值
$fred = 17; $barney = "hello"; $barney = $fred + 3;# 將$fred 的值加上三賦給$barney (20) $barney= $barney*2;#將變量$barney 乘 2 再賦給$barney (40)
二元賦值操作符
$fred = $fred + 5; #沒(méi)有用二元賦值操作符 $fred+=5; #利用二元賦值操作符 $barney = $barney*3; $barney*=3; $str = str . ""; #$str 后接空格; $str .= ""; #同上
print 輸出
print "hello world\n"; #輸出 hello world,后接換行符 print "The answer is", 6*7, ".\n"
字符串中引用標(biāo)量變量
$meal = "brontosaurus steak" ; $barney = "fred ate a $meal"; $barney = 'fred ate a'.$meal; # 同上
if 控制結(jié)構(gòu)
if ($name gt 'fred') { print "$name’comes after 'fred' in sorted order.\n"; }
Boolean 值
perl 沒(méi)有專(zhuān)門(mén)的 Boolean 值,, 真假值這樣判斷:
- 如果值為數(shù)字,0 是 false;其余為真
- 如果值為字符串,則空串(‘)為 false;其余為真
- 如果值的類(lèi)型既不是數(shù)字又不是字符串,則將其轉(zhuǎn)換為數(shù)字或字符串后再利用上述規(guī)則
這些規(guī)則中有一個(gè)特殊的地方。由于字符串'0' 和數(shù)字 0 有相同的標(biāo)量值,Perl 將它們相同看待,。也就是說(shuō)字符串 '0' 是唯一一個(gè)非空但值為 0 的串,。
用戶(hù)的輸入 <STDIN>
chomp 操作
$text = "a line of text\n"; # 也可以由<STDIN>輸入 chomp($text); #去掉換行符(\n)。
一步執(zhí)行:
chomp ($text = <STDIN>); #讀入,但不含換行符
chomp 是一個(gè)函數(shù),。作為一個(gè)函數(shù),它有一個(gè)返回值,為移除的字符的個(gè)數(shù),。這個(gè)數(shù)字基本上沒(méi)什么用:
$food = <STDIN>; $betty = chomp $food; #得到值 1
如上,在使用 chomp 時(shí),可以使用或不使用括號(hào)()。這又是 Perl 中的一條通用規(guī)則:除非移除它們時(shí)含義會(huì)變,否則括號(hào)是可以省略的,。
while 控制結(jié)構(gòu)
$count = 0; while ($count < 10) { $count + = 2; print "count is now $count\n"; }
undef 值
變量被賦值之前使用它會(huì)有什么情況發(fā)生呢?通常不會(huì)有什么嚴(yán)重的后果,。變量在第一次賦值前有一個(gè)特殊值 undef, 按照 Perl 來(lái)說(shuō)就是:"這里什么也沒(méi)有,請(qǐng)繼續(xù)"。如果這里的“什么也沒(méi)有”是一些“數(shù)字”,則表現(xiàn)為 0,。如果是“字符串”,則表現(xiàn)為空串,。但 undef 既非數(shù)字也非字符串,它是另一種標(biāo)量類(lèi)型,。
defined 函數(shù)
能返回 undef 的操作之一是行輸入操作,<STDIN>。通常,它會(huì)返回文本中的一行,。但如果沒(méi)有更多的輸入,如到了文件的結(jié)尾,則返回 undef,。要分辨其是 undef 還是空串,可以使用 defined 函數(shù), ,如果其參數(shù)是 undef 值就返回 false,,其他值返回 true,。
$madonna = <STDIN>; If ($defined ($madonna)){ print "The input was $madonna"; } else { print "No input available!\n; }
如果想聲明自己的 undef 值,可以使用 undef:
$madonna = undef ; #同$madonna 從未被初始化一樣。
列表和數(shù)組
#!/usr/bin/env perl -w $fred[0] = "yabba"; $fred[1] = "dabba"; $fred[2] = "doo"; print @fred; #print @fred."\n";
qw 簡(jiǎn)寫(xiě)
qw ! fred barney betty wilma dino ! qw# fred barney betty wilma dino # #有些像注釋 qw( fred barney betty wilma dino ) ...
列表賦值
($fred, $barney, $dino) = ("flintstone", "rubble", undef); ($fred, $barney) = qw <flintstone rubble slate granite>; #兩個(gè)值被忽略了 ($rocks[0],$rocks[1],$rocks[2],$rocks[3]) = qw/talc mica feldspar quartz/;
當(dāng)想引用這個(gè)數(shù)組時(shí), Perl 有一種簡(jiǎn)單的寫(xiě)法,。在數(shù)組名前加@(后沒(méi)有中括號(hào)) 來(lái)引用整個(gè)數(shù)組,。 你可以把他讀作 "all of the "(所有的)”,所以@rocks 可以讀作 "all of the rocks(所有的石頭)"。其在賦值運(yùn)算符左右均有效:
@rocks = qw / bedrock slate lava /; @tiny = (); #空表 @giant = 1..1e5; #包含 100,000 個(gè)元素的表 @stuff = (@giant, undef, @giant); #包含 200,001 個(gè)元素的表 @dino = "granite"; @quarry = (@rocks, "crushed rock", @tiny, $dino);
pop 和 push 操作
@array = 5..9; $fred = pop(@array); #$fred 得到 9,@array 現(xiàn)在為(5,6,7,8) $barney = pop @array; #$barney gets 8, @array 現(xiàn)在為(5,6,7) pop @array; #@array 現(xiàn)在為(5,6)(7 被丟棄了)
push(@array,0); #@array 現(xiàn)在為(5,6,0) push @array,8; #@array 現(xiàn)在為(5,6,0,8) push @array,1..10; #@array 現(xiàn)在多了 10 個(gè)元素 @others =qw/9 0 2 1 0 /; push @array,@others; #@array 現(xiàn)在又多了 5 個(gè)元素(共有 19 個(gè))
shift 和 unshift 操作
push 和 pop 對(duì)數(shù)組的末尾進(jìn)行操作(或者說(shuō)數(shù)組右邊有最大下標(biāo)的元素,這依賴(lài)于你是怎樣思考的),。相應(yīng)的, unshift 和 shift 對(duì)一個(gè)數(shù)組的開(kāi)頭進(jìn)行操作(數(shù)組的左端有最小下標(biāo)的元素) ,。下面是一些例子:
@array = qw# dino fred barney #; $m = shift (@array); #$m 得到 "dino", @array 現(xiàn)在為 ("fred", "barney") $n = shift @array; #$n 得到 "fred", @array 現(xiàn)在為 ("barney") shift @array; #@array 現(xiàn)在為空 $o = shift @array; #$o 得到 undef, @arry 仍為空 unshift(@array,5); #@array 現(xiàn)在為(5) unshift @array,4; #@array 現(xiàn)在為(4,5) @others = 1..3; unshift @array, @others; #array 現(xiàn)在為(1,2,3,4,5)
和 pop 類(lèi)似,如果其數(shù)組變量為空,則返回 undef,。
字符串中引用數(shù)組
和標(biāo)量類(lèi)似,數(shù)組也可以插入雙引號(hào)的字符串中,。插入的數(shù)組元素會(huì)自動(dòng)由空格分開(kāi):
@rocks = qw{ flintstone slate rubble }; print "quartz @rocks limestone\n"; #輸出為 5 種 rocks 由空格分開(kāi)
foreach 控制結(jié)構(gòu)
foreach $rock (qw/ bedrock slate lava /) { print "One rock is $rock.\n" ; #打印出 3 種 rocks }
這里的$rock不是這些列表元素中的一個(gè)拷貝而是這些元素本身
最常用的默認(rèn)變量 : $_
如果在 foreach 循環(huán)中省略了控制變量,那 Perl 會(huì)使用其默認(rèn)的變量:$_。除了其不尋常的名字外,這和普通變量類(lèi)似,如下面代碼所示:
foreach(1..10){ #使用默認(rèn)的變量$_ print "I can count to $_!\n"; }
$_ = "Yabba dabba doo\n"; print; # 打印出默認(rèn)變量 $_
reverse 操作
reverse(逆轉(zhuǎn))操作將輸入的一串列表(可能是數(shù)組)按相反的順序返回,。
@fred = 6 .. 10; @barney = reverse (@fred); #得到 10,9,8,7,6 @wilma = reverse 6 . .10; #同上,沒(méi)有使用額外的數(shù)組 @fred = reverse @fred; #將逆轉(zhuǎn)過(guò)的字符串存回去
sort 操作
@rocks = qw/ bedrock slate rubble granite /; @sorted = sort(@rocks); #得到 bedrock, granite, rubble, slate
標(biāo)量和列表上下文
42 + something #something 必須是標(biāo)量 sort something #something 必須是列表
@people = qw( fred barney betty ); @sorted = sort @people; #列表內(nèi)容: barney , betty, fred $number = 42 + @people; #標(biāo)量?jī)?nèi)容:42+3,得到 45
另一個(gè)例子是 reverse,。在列表 context 中,它返回反轉(zhuǎn)的列表。在標(biāo)量 context 中,返回反轉(zhuǎn)的字符串(或者將反轉(zhuǎn)的結(jié)果串成一個(gè)字符串):
@backwards = reverse qw / yabba dabba doo /; #返回 doo, dabba, yabba $backwards = reverse qw/ yabba dabba doo /; #返回 oodabbadabbay
在列表 Context 中使用 Scalar-Producing 表達(dá)式
如果一個(gè)表達(dá)式不是列表值,則標(biāo)量值自動(dòng)轉(zhuǎn)換為一個(gè)元素的列表:
@fred = 6*7; @barney = "hello" . '' . "world";
強(qiáng)制轉(zhuǎn)換為標(biāo)量 Context
偶爾,你可能需要標(biāo)量 context 而 Perl 期望的是列表,。這種情況下,可以使用函數(shù) scalar,。它不是一個(gè)真實(shí)的函數(shù)因?yàn)槠鋬H是告訴 Perl 提供一個(gè)標(biāo)量 context:
@rocks = qw(talc quartz jade obsidian); print "How many rocks do you have?\n; print "I have " @rocks, "rocks!\n"; # 錯(cuò)誤,輸出 rocks 的名字 print "I have " scalar @rocks, "rocks!\n; # 正確,輸出其數(shù)字
<STDIN> 在列表 Context 中
@lines = <STDIN>; #將輸入讀入列表 context 中 chomp (@lines = <STDIN>); #讀入所有的行,不包括換行符
子程序
使用 sub 定義子程序
sub marine { $n + = 1; #全局變量$n print "Hello, sailor number $n!\n"; }
調(diào)用子程序
&marine; #輸出 Hello, sailor number 1! &marine; #輸出 Hello, sailor number 2! &marine; #輸出 Hello, sailor number 3! &marine; #輸出 Hello, sailor number 4!
通常有括號(hào),即便參數(shù)為空。子程序?qū)⒗^承調(diào)用者的 @_ 的值,。
參數(shù)
$n = &max(10,15); # 此子程序有 2 個(gè)參數(shù)
此參數(shù)列表被傳到子程序中;這些參數(shù)可以被子程序使用,。當(dāng)然,這些參存放在某個(gè)地方,在 Perl 中,會(huì)自動(dòng)將此參數(shù)列表(此參數(shù)列表的另一個(gè)名字)自動(dòng)存放在一個(gè)叫做@_的數(shù)組中。子程序可以訪(fǎng)問(wèn)次數(shù)組變量來(lái)確定此參數(shù)的個(gè)數(shù)以及其值,。這也就是說(shuō)此子程序參數(shù)的第一個(gè)值存放在$_[0]中,第二個(gè)存放在$_1,依次類(lèi)推,。但必須強(qiáng)調(diào)的是這些變量和 $_ 這個(gè)變量沒(méi)有任何關(guān)系,如$dino3(數(shù)組 @dino 的一個(gè)元素)和$dino 的關(guān)系一樣。這些參數(shù)必須存放在某個(gè)數(shù)組變量中, Perl 存放在@_這個(gè)變量中,。
sub max{ if($_[0] > $_[1]) { $_[0]; } else { $_[1]; } }
my 變量
foreach (1..10){ my($square) = $_*$_; #本循環(huán)中的私有變量 print "$_ squared is $squrare.\n"; }
變量$square 是私有的,僅在此塊中可見(jiàn);在本例中,此塊為 foreach 循環(huán)塊,。
當(dāng)然,my 操作不會(huì)改變賦值參數(shù)的 context:
my ($num) = @_; # 列表 context, 同($sum) = @_; my $num = @_; # 標(biāo)量 context,同$num = @_;
使用 strict Pragma
use strict; #迫使采用更嚴(yán)格的檢測(cè)
省略 &
有些地方調(diào)用子程序可以不要 &
my @cards = shuffle(@deck_of_cards); # &是不必要的
輸入和輸出
從標(biāo)準(zhǔn)輸入設(shè)備輸入
從標(biāo)準(zhǔn)輸入設(shè)備輸入是容易的。使用<STDIN>,。在標(biāo)量 context 中它將返回輸入的下一行:
$line = <STDIN>; #讀入下一行; chomp($line); #去掉結(jié)尾的換行符 chomp($line=<STDIN>) #同上,更常用的方法
于,行輸入操作在到達(dá)文件的結(jié)尾時(shí)將返回 undef,這對(duì)于從循環(huán)退出時(shí)非常方便的:
while (defined($line = <STDIN>)) { print "I saw $line"; }
從 <> 輸入
尖括號(hào)操作(<>)是一種特殊的行輸入操作,。其輸入可由用戶(hù)選擇
$n = 0; while (defined($line = <>)) { $n += 1; chomp($line); print "$n $line\n"; }
while (<>) { chomp; print "It was $_ that I saw!\n"; }
調(diào)用參數(shù)
技術(shù)上講,<>從數(shù)組@ARGV 中得到調(diào)用參數(shù),。這個(gè)數(shù)組是 Perl 中的一個(gè)特殊數(shù)組,其包含調(diào)用參數(shù)的列表,。 換句話(huà)說(shuō),這和一般數(shù)組沒(méi)什么兩樣 (除了其名字有些特別: 全為大寫(xiě)字母) ,程序開(kāi)始運(yùn)行時(shí),調(diào)用參數(shù)已被存在@ARGV 之中了。
輸出到標(biāo)準(zhǔn)輸出設(shè)備
print @array; #打印出元素的列表 print "@array"; #打印一個(gè)字符串(包含一個(gè)內(nèi)插的數(shù)組)
第一個(gè)語(yǔ)句打印出所有的元素,一個(gè)接著一個(gè),其中沒(méi)有空格,。第二個(gè)打印出一個(gè)元素,它為@array 的所有元素,其被存在一個(gè)字符串中,。也就是說(shuō),打印出@array 的所有元素,并由空格分開(kāi)。如果@array 包含 qw /fred barney betty /,則第一個(gè)例子輸出為:fredbarneybetty,而第二個(gè)例子輸出為 fred barney betty(由空格分開(kāi)),。
使用 printf 格式化輸出
printf "Hello, %s : your password expires in %d days!\n", $user, $days_to_die; printf "%6f\n" 42; # 輸出為 ○○○○ 42 (○此處指代空格) printf "%23\n",2e3+1.95; # 2001
數(shù)組和 printf
my @items = qw( wilma dino pebbles ); my $format = "The items are:\n". ("%10s\n" x @items); printf $format, @items;
等同:
printf "The items are:\n". ("%10s\n"x @items), @items;
本處 @items 有兩個(gè)不同的 context (上下文),,第一次表示元素個(gè)數(shù),第二次表示列表中的所有元素,。
句柄 (即文件描述符)
Perl 自身有六個(gè)文件句柄 : STDIN,STDOUT,STDERR,DATA,ARGV,ARGVOUT
文件句柄的打開(kāi)
open CONFIG, "dino" ; open CONFIG, "<dino" ; open BEDROCK, ">fred" ; open LOG, ">>logfile" ;
Perl 的新版本中(從 Perl5.6 開(kāi)始),,open 支持“3 參數(shù)”類(lèi)型:
open CONFIG, "<", "dino"; open BEDROCK, ">", $file_name; open LOG, ">>", &logfile_name();
Bad 文件句柄
關(guān)閉文件句柄
close BEDROCK;
嚴(yán)重錯(cuò)誤和 die
可以和 C 中使用 perror 類(lèi)似,用 die 函數(shù):
if (!open LOG, ">>logfile") { die "Cannot create logfile:$!"; }
使用文件句柄
if (!open PASSWD, "/etc/passwd") { die "How did you get logged in?($!)"; } while (<PASSWD>) { chomp; ... }
哈希
什么是哈希
和 Python 的字典一樣
哈希元素的存取
$hash {$some_key}
作為整體的 hash
要引用整個(gè) hash,使用百分號(hào)(“%” )作為前綴,。
%some_hash = ("foo",35, "bar", 12.4, 2.5, "hello", "wilma", 1.72e30, "betty", "bye\n");
hash 的值(在列表 context 中)是一個(gè) key/value 對(duì)的列表 :
@array_array = %some_hash;
哈希賦值
%new_hash = %old_hash; %inverse_hash = reverse %any_hash;
大箭頭符號(hào) (=>)
my %last_name = ( "fred” => "flintstone", "dino" => undef, "barney" => "rubble", "betty" => "rubble", );
哈希函數(shù)
keys 和 values
my %hash = ("a" => 1, "b" => 2, "c" => 3); my @k = keys %hash; my @v = values %hash;
each 函數(shù)
while (($key, $value) = each %hash) { print "$key => $value\n"; }
exists 函數(shù)
if (exists $books{$dino}) { print "Hey, there's a libaray card for dino!\n"; }
delete 函數(shù)
my $person = "betty"; delete $books{$person}; # 將$person 的借書(shū)卡刪除掉
正則表達(dá)式
簡(jiǎn)單的模式
$_ = "yabba dabba doo"; if (/abba/) { print "It matched!\n"; }
所有在雙引號(hào)中的轉(zhuǎn)義字符在模式中均有效,因此你可以使用 /coke\tsprite/
來(lái)匹配 11 個(gè)字符的字符串 coke, tab(制表符),sprite,。
元字符
. ? + *
模式中的分組
/fred+/ # 只能匹配 fredddddd 等 /(fred)+/ # 能匹配 fredfredfred 等 /(fred)*/ # 可以匹配 "hello,world", 因?yàn)?* 是匹配前面的 0或多次
選擇符 (|)
/fred|barney|betty/ /fred( |\t)+barney/
字符類(lèi)
指 [] 中的一列字符,。
字符類(lèi)的簡(jiǎn)寫(xiě)
\d == [0-9] \w == [A-Za-z0-9_] \s == [\f\t\n\r] # 格式符(form-feed),、制表符(tab)、換行符,、回車(chē)
簡(jiǎn)寫(xiě)形式的補(bǔ)集
\D == [^\d] \W == [^\w] \S == [^\s]
可以組合:
[\d\D] # 任何數(shù)字和任何非數(shù)字,,可以匹配所有字符!比如 . 是不能匹配所有字符的 [^\d\D] # 無(wú)用
正則表達(dá)式的應(yīng)用
使用 m// 匹配
同 qw // 一樣,,可以使用任何成對(duì)字符,,比如可以使用 m(fred), m<fred>, m{fred}, m[fred],或者 m,fred,, m!fred!, m^fred^。
如果使用 / 作為分隔符,,可以省略前面的 m
如果使用配對(duì)的分隔符, 那不用當(dāng)心模式內(nèi)部會(huì)出現(xiàn)這些分隔符, 因?yàn)橥ǔDJ絻?nèi)部的分隔符也是配對(duì)的,。 因此, m(fred(.*)barney), m{\w{2,}}, m[wilma[\n \t]+betty]是正確的。對(duì)于尖括號(hào)(<和>),它們通常不是配對(duì)的,。如模式 m{(\d+)\s*>=?\s*(\d+)},如果使用尖括號(hào),模式中的尖括號(hào)前因當(dāng)使用反斜線(xiàn)(\), 以免模式被過(guò)早的結(jié)束掉。
可選的修飾符
不區(qū)分大小寫(xiě): /i
if (/yes/i) { #大小寫(xiě)無(wú)關(guān) print "In that case, I recommend that you go bowling.\n"; }
匹配任何字符: /s
使用/s 這個(gè)修飾符,,它將模式中點(diǎn) (.) 的行為變成同字符類(lèi) [\d\D] 的行為類(lèi)似:可以匹配任何字符,包括換行符,。從下例中可見(jiàn)其區(qū)別:
$_ = "I saw Barney\ndown at the bowing alley\nwith Fred\nlast night.\n; if (/Barney.*Fred/s) { print "That string mentions Fred after Barney!\n"; }
添加空格: /x
/x 修飾符,允許你在模式中加入任何數(shù)量的空白,以方便閱讀:
/-?\d+\.?\d*/ # 這是什么含義? / -? \d+ \.? \d* /x # 要好些
Perl 中,注釋可以被作為空白,因此使用/x,可以在模式中加上注釋?zhuān)?/p>
/ -? #可選的負(fù)號(hào) d+ #小數(shù)點(diǎn)前一個(gè)或多個(gè)十進(jìn)制數(shù)字 \.? #可選的小數(shù)點(diǎn) \d* #小數(shù)點(diǎn)后一些可選的十進(jìn)制數(shù)字 /x #模式結(jié)束
使用多個(gè)修飾符
if (/barney.*fred/is/){ # /i 和/s print "That string mentions Fred after Barney!\n"; }
錨定
^ 開(kāi)頭 $ 結(jié)尾
注意:/^fred$/能同時(shí)匹配上 "fred" 和 "fred\n"。
/^\s*$/ # 匹配空行
詞錨定: \b
/\bfred\b/ 可以匹配單詞 "fred", 但不能匹配 "frederick"
綁定操作符: =~
對(duì) $_ 進(jìn)行匹配只是默認(rèn)的行為, 使用綁定操作符 ( =~ ) 將告訴 Perl 將右邊的模式在左邊的字符串上進(jìn)行匹配,而非對(duì) $_ 匹配,。
my $some_other = "I dream of betty rubble."; if ($some_other =~ /\brub/) { print "Aye, there's the rub.\n"; }
綁定操作符優(yōu)先級(jí)較高:
my $likes_perl = <STDIN> =~ /\byes\b/i;
匹配變量
$_ = "Hello there, neighbor"; if (/\s(\w+),/) { #空格和逗號(hào)之間的詞 print "The word was $1\n"; }
自動(dòng)匹配變量
$& # 整個(gè)被匹配的部分 $` # 匹配部分的前一部分存放在$`之中 $' # 后一部分被存到$'
使用正則表達(dá)式處理文件
使用 s/// 進(jìn)行替換
$_ = "He's out bowling with Barney tonight."; s/Barney/Fred/; # Barney 被 Fred 替換掉 print "$_\n"; #接上例:現(xiàn)在 $_ 為 "He's out bowling with Fred tonight." s/Wilma/Betty/; # 用 Wilma 替換 Betty(失敗) s/with (\w+)/agaist $1's team/; print "$_\n"; # 為 "He's out bowling against Fred's team tonight.";
使用 /g 進(jìn)行全局替換
$_ = "home, sweet home!"; s/home/cave/g; print "$_\n"; # "cave, sweet cave!"
全局替換的一個(gè)常用地方是將多個(gè)空格用單個(gè)空格替換掉:
$_ = "Input data\t may have extra whitespace."; s/\s+/ /g; # 現(xiàn)在是 "Input data may have extra whitespace."
現(xiàn)在已經(jīng)知道怎樣去掉多余的空格,那怎樣去掉開(kāi)頭和結(jié)尾的空白呢?這是非常容易的:
s/^\s+//; #將開(kāi)頭的空白去掉 s/\s+$//; #將結(jié)尾的空白去掉 s/^\s+|\s+$//g; #將開(kāi)頭,結(jié)尾的空白去掉
不同的分隔符
如同 m//和 qw//一樣,我們也可以改變 s///的分隔符,。但這里使用了3個(gè)分隔符,因此有些不同。
s#^https://#http://#;
如果使用的是配對(duì)的字符,也就是說(shuō)其左字符和右字符不的,則必需使用兩對(duì):一對(duì)存放模式,一對(duì)存放替換的字符串,。此時(shí),分隔符甚至可以是不同的,。事實(shí)上,分隔符還可以使用普通的符號(hào)(非配對(duì)的)。下面三例是等價(jià)的:
s{fred}{barney}; s[fred](barney); s<fred>#barney#;
可選的修飾符
除了/g 修飾符外,,替換操作中還可以使用 /i ,, /x , 和 /s, 這些在普通的模式匹配中已經(jīng)出現(xiàn)過(guò)的修飾符,。其順序是無(wú)關(guān)緊要的,。
s#wilma#Wilma#gi; # 將所有的 WilmA,或者 WILMA 等等,由 Wilma 替換掉 s{_ _END_ _.*}{ }s; # 將 END 標(biāo)記及其后面的行去掉
綁定操作
同 m// 一樣,我們也可以通過(guò)使用綁定操作符改變 s/// 的替換目標(biāo):
$file_name =~ s#^.*###s; # 將$file_name 中所有的 Unix 類(lèi)型的路徑去掉
大小寫(xiě)替換
有時(shí),希望確保被替換的字符串均是大寫(xiě)的(或者不是,視情況而定) 。這在 Perl 中只需使用某些修飾符就能辦到,。 \U 要求緊接著的均是大寫(xiě):
$_ = "I saw Barney with Fred."; s/(fred|barney)/\U$1/gi; # $_ 現(xiàn)在是 "I saw BARNEY with FRED."
同樣,也可以要求后面的均為小寫(xiě) \L :
s/(fred)|barney/\L$1/gi; #$_現(xiàn)在是 "I saw barney with fred."
默認(rèn)時(shí),會(huì)影響到剩余的(替換的)字符串,。可以使用 \E 來(lái)改變這種影響:
s/(\w+) with (\w+)/\U$2\E with $1/I; # $1 現(xiàn)在是 "I saw FRED with barney."
使用小寫(xiě)形式時(shí)( \l 和 \u ),只作用于下一個(gè)字符:
s/ (fred|barney)/\u$1/ig; #$_現(xiàn)在是 "I saw FRED with Barney."
也可以同時(shí)使用它們,。如使用\u 和\L 表示 "第一個(gè)字母大寫(xiě),其它字母均小寫(xiě)",。 \L 和\u 可以按任意順序出現(xiàn)。Larry 意識(shí)到人們有時(shí)可能按相反順序使用它們,因此他將 Perl 設(shè)計(jì)成,在這兩種情況下都是將第一個(gè)字母大寫(xiě),其余的小寫(xiě),。 Larry 是個(gè)非常好的人,。
s/(fred|barney)/\u\L$1/ig; #$_現(xiàn)在為 "I saw Fred with Barney."
這些在替換中出現(xiàn)的大小寫(xiě)轉(zhuǎn)換的修飾符,也可在雙引號(hào)中使用:
print "Hello, \L\u$name\E, would you like to play a game?\n";
split 操作
另一個(gè)使用正則表達(dá)式的操作是 split , 它根據(jù)某個(gè)模式將字符串分割開(kāi)。這對(duì)于由制表符分割開(kāi),冒號(hào)分割開(kāi),空白分割開(kāi),或者任意字符分割開(kāi)的數(shù)據(jù)是非常有用的,。任何可在正則表達(dá)式之中 (通常,,是一個(gè)簡(jiǎn)單的正則表達(dá)式) 指定分離符 (separator) 的地方,均可用 split。其形式如下:
@fields = split /separtor/, $string;
@fields = split /:/, "abc:def:g:h"; # 返回 ("abc", "def", "g", "h")
/\s+/ 這個(gè)模式分隔符非常常見(jiàn):
my $some_input = "This is a \t test.\n"; my @args = split /\s+/, $some_input; # ("This", "is", "a", "test." )
默認(rèn)時(shí),split 對(duì)$_操作,模式為空白:
my @fields = split; # 同 split /\s+/, $_;
join 函數(shù)
join 函數(shù)不使用模式,但它完成同 split 相反的操作:split 將一個(gè)字符串分割開(kāi),而 join 函數(shù)將這些分割的部分組合成一個(gè)整體,。join 函數(shù)類(lèi)似于 :
my $result = join $glue, @pieces;
join 函數(shù)的第一個(gè)參數(shù)是粘合元素(glue),它可以是任意字符串,。剩下的參數(shù)是要被粘合的部分。join 將粘合元素添加在這些部分之間,并返回其結(jié)果:
my $x = join ":", 4, 6, 8, 10, 12; #$x 為 "4:6:8:10:12"
列表上下文中的 m//
在列表 context 中使用模式匹配(m//)時(shí),如果匹配成功返回值為內(nèi)存變量值的列表;如果匹配失敗則為空列表:
$_ = "Hello there, neighbor!"; my($first, $second, $third) =/(\S+) (\S+), (\S+)/; print “$second is my $third\n” ;
在 s/// 中介紹的 /g 修飾符也可在 m// 中使用,它允許你在字符串中的多處進(jìn)行匹配,。在這里,由括號(hào)括起來(lái)的模式將在每一次匹配成功時(shí)返回其內(nèi)存中所存放的值:
my $text = "Fred dropped a 5 ton granite block on Mr. Slate"; my @words = ($text =~ /([a-z]+)/ig); print "Result: @words\n"; #Result: Fred dropped a ton granite block on Mr slate
如果有不止一對(duì)括號(hào),每一次返回不止一個(gè)字符串,。例如將字符串放入 hash 中,如下:
my $data = "Barney Rubble Fred Flintstone Wilma Flintstone"; my %last_name = ($data =~ / (\w+)\S+(\w+)/g);
每當(dāng)模式匹配成功時(shí),將返回一對(duì)值。這些一對(duì)一對(duì)的值就成了 hash 中的 key/value 對(duì),。
更強(qiáng)大的正則表達(dá)式
非貪婪的數(shù)量詞
各符號(hào)對(duì)應(yīng)的非貪婪匹配:
+ --> /fred.+?barney/ * --> s#<BOLD>(.*?)</BOLD>#$1#g; {5,10}? {8,}? ? --> ??
在命令行進(jìn)行修改
$ perl –p –i.bak –w –e 's/Randall/Randal/g' fred*.dat
-p 要求 Perl 為你寫(xiě)一個(gè)程序,。它算不上是一個(gè)完整的程序;看起來(lái)有些像下面的:
while(<>) { print; }
更多控制結(jié)構(gòu)
unless 控制結(jié)構(gòu)
在 if 控制結(jié)構(gòu)中,只有條件為真時(shí),才執(zhí)行塊中的代碼。如果你想在條件為假時(shí)執(zhí)行,可以使用 unless:
unless ($fred =~ /^[A-Z_]\w*$/i) { print "The value of \$fred doesn't look like a Perl identifier name.\n"; }
unless 的含義是:除非條件為真,否則執(zhí)行塊中的代碼,。這和在 if 語(yǔ)句的條件表達(dá)式前面加上!(取反)所得結(jié)果是一樣的,。
另一種觀(guān)點(diǎn)是,可以認(rèn)為它自身含有 else 語(yǔ)句。如果不太明白 unless 語(yǔ)句,你可以把它用 if 語(yǔ)句來(lái)重寫(xiě)(頭腦中,或者實(shí)際的重寫(xiě)) :
if ($fred =~ /^[A-Z_]\w*$/i) { #什么也不做 } else { print "The value of \$fred doesn't look like a Perl identifier name.\n"; }
unless 和 else 一起
unless ($mon =~ /^Feb/) { print "This month has at least thirty days.\n"; } else { print "Do you see what's going on here?\n"; }也可以同時(shí)使用它們
until 控制結(jié)構(gòu)
有時(shí),希望將 while 循環(huán)的條件部分取反,。此時(shí),可以使用 until:
until ($j > $i) { $j *= 2; }
表達(dá)式修飾符
為了得到更緊湊的形式,表達(dá)式后可以緊接控制修飾語(yǔ),。如,if 修飾語(yǔ)可以像 if 塊那樣使用:
print "$n is a negative number.\n" if $n<0;
等同下面代碼:
if ($n < 0) { print "$n is a negative number.\n"; }
雖然被放在后面,條件表達(dá)式也是先被求值的,。這和通常的的從左到右的順序相反,。要理解 Perl 代碼,應(yīng)當(dāng)像 Perl 內(nèi)部的編譯器那樣,將整個(gè)語(yǔ)句讀完,來(lái)其具體的含義。
還有一些其它的修飾語(yǔ):
&error ("Invalid input") unless &valid($input); $i *= 2 unitl $i > $j; print "", ($n += 2) while $n <10; &greet($_) foreach @person;
The Naked Block 控制結(jié)構(gòu)
被稱(chēng)為“裸的”塊是指沒(méi)有關(guān)鍵字或條件的塊,。假如有一個(gè) while 循環(huán),大致如下:
while(condition) { body; body; body; }
將關(guān)鍵字 while 和條件表達(dá)式去掉,則有了一個(gè)“裸的”塊:
{ body; body; body; }
“裸的”塊看起來(lái)像 while 或 foreach 循環(huán),除了它不循環(huán)外;它執(zhí)行“循環(huán)體”一次,然后結(jié)束,。它根本就沒(méi)循環(huán)!
你也可能在其它地方使用過(guò)“裸的”塊,這通常是為臨時(shí)變量提供作用域:
{ print "Please enter a number:"; chomp(my $n = <STDIN>); my $root = sqrt $n; #計(jì)算平方根 print "The square root of $n is $root.\n"; }
elsif 語(yǔ)句
自增和自減 (同C)
my @people = qw{ fred barney fred Wilma dino barney fred pebbles}; my %count; #新的空的 hash $count{$_}++ foreach @people; #根據(jù)情況創(chuàng)建新的 keys 和 values
第一次執(zhí)行 foreach 循環(huán)時(shí),$count{$_}自增1。此時(shí),$count{"fred"} 從 undef (由于之前 hash 中不存在)變成1,。第二次執(zhí)行 foreach 循環(huán)時(shí),$count{"barney"} 變成1; 接著,$count{"fred"} 變成2,。每執(zhí)行一次循環(huán),%count 中的某個(gè)元素增1,或者新元素被創(chuàng)建,。循環(huán)結(jié)束時(shí),$count{"fred"} 值為3。這提供了一種查看某個(gè)元素是否存在于列表之中,以及其出現(xiàn)次數(shù)的方法,。
for 控制結(jié)構(gòu) (同C)
for (initialization; test; increment) { body; body; }
foreach 和 for 的關(guān)系
對(duì)于 Perl 解析器(parser)而言, 關(guān)鍵字 foreach 和 for 是等價(jià)的,。也就是說(shuō), Perl 遇見(jiàn)其中之一時(shí), 和遇見(jiàn)另一個(gè)是一樣的。Perl 通過(guò)括號(hào)能理解你的目的,。如果其中有兩個(gè)分號(hào),則是 for 循環(huán)(和我們剛講的類(lèi)似); 如果沒(méi)有,則為 foreach 循環(huán):
for (1..10) { #實(shí)際上是 foreach 循環(huán),從1到10 print "I can count to $_!\n"; }
這實(shí)際是一個(gè) foreach 循環(huán),但寫(xiě)作 for,。除了本例外,本書(shū)后面均寫(xiě)作 foreach。在實(shí)際代碼中,你覺(jué)得 Perler 會(huì)輸入這四個(gè)多余的字符嗎?除了新手外,一般均寫(xiě)作 for,你也應(yīng)當(dāng)像 Perl 那樣,通過(guò)查看括號(hào)內(nèi)分號(hào)個(gè)數(shù)來(lái)判斷,。
循環(huán)控制
last 操作
last 會(huì)立刻結(jié)束循環(huán),。(這同 C 語(yǔ)言或其它語(yǔ)言中的 "break" 語(yǔ)句類(lèi)似)。它從循環(huán)塊中“緊急退出” ,。當(dāng)執(zhí)行到 last,循環(huán)即結(jié)束,如下例 :
#輸出所有出現(xiàn) fred 的行,直到遇見(jiàn) _ _END_ _標(biāo)記 while(<STDIN>){ if(/_ _ END_ _/){ #這個(gè)標(biāo)記之后不會(huì)有其它輸入了 last; }elsif(/fred/){ print; } } ##last 跳轉(zhuǎn)到這里##
next 操作 (同C的中 continue)
redo 操作
循環(huán)控制的第三個(gè)操作是 redo,。它會(huì)調(diào)到當(dāng)前循環(huán)塊的頂端,不進(jìn)行條件表達(dá)式判斷以及接著本次循環(huán)。(在 C 或類(lèi)似語(yǔ)言中沒(méi)有這種操作)下面是一個(gè)例子:
# 輸入測(cè)試 my @words = qw{ fred barney pebbles dino Wilma betty }; my $errors = 0; foreach(@words){ ##redo 跳到這里## print "Type the word '$_' : "; chomp(my $try = <STDIN>); if($try ne $_){ print "sorry -- That's not right.\n\n"; $errors++; redo; #跳轉(zhuǎn)到循環(huán)頂端 } } print "You've completed the test, with $errors errors.\n";
標(biāo)簽塊
要給循環(huán)體加上標(biāo)簽,可在循環(huán)前面加上標(biāo)簽和冒號(hào),。在循環(huán)體內(nèi),可以根據(jù)需要在 last, next,或n者 redo 后加上標(biāo)簽名,如:
LINE: while(<>){ foreach (split){ last LINE if /_ _END_ _/; # 退出 LINE 循環(huán) ... } }
邏輯操作符 && 和 ||
短路操作符
和 C(以及類(lèi)似的語(yǔ)言)不同的地方是,短路操作的結(jié)果是最后被執(zhí)行語(yǔ)句的返回值,而非僅僅是一個(gè) Boolean 值,。
my $last_name = $last_name{$someone} || '(No last name)';
三元操作符 ?:
當(dāng) Larry 決定 Perl 應(yīng)當(dāng)具有哪些操作符時(shí),他不希望以前的 C 程序員在 Perl 中找不到 C 中出現(xiàn)過(guò)的操作符,因此他在 Perl 中實(shí)現(xiàn)了所有 C 的操作符。這意味著 C 中最容易混淆的操作符:三元操作符(?:)也被移植過(guò)來(lái)了,。雖然它帶來(lái)了麻煩,但有時(shí)也是非常有用的,。
Express ? if_true_expr : if_false_expr
my $size = ($width < 10 ) ? "small": ($width < 20) ? "medium": ($width < 50) ? "large": "extra_large"; #default
控制結(jié)構(gòu): 使用部分求值的操作符
前面三個(gè)操作符,均有一個(gè)共同的特殊性質(zhì):根據(jù)左側(cè)的值(true 或 false),來(lái)判斷是否執(zhí)行右側(cè)代碼。有時(shí)會(huì)被執(zhí)行,有時(shí)不會(huì),。由于這個(gè)理由,這些操作符有時(shí)叫做部分求值(partial-evaluation)操作符,因?yàn)橛袝r(shí)并非所有的表達(dá)式均被執(zhí)行,。部分求值操作符是自動(dòng)的控制結(jié)構(gòu)。并非 Larry 想引入更多的控制結(jié)構(gòu)到 Perl 中來(lái),。而是當(dāng)他決定將這些部分求值操作符引進(jìn) Perl 后,它們自動(dòng)成了控制結(jié)構(gòu),。畢竟,任何可以激活或解除某塊代碼的執(zhí)行即被稱(chēng)作控制結(jié)構(gòu)。
文件校驗(yàn)
文件檢測(cè)操作
如果程序會(huì)建立新的文件,在程序創(chuàng)建新文件之前,我們應(yīng)先確定是否存在同名的文件,以免重要數(shù)據(jù)被覆蓋掉,。對(duì)于這種問(wèn)題,我們可以使用– 選項(xiàng),檢測(cè)是否存在相同名字的文件:
die "Oops! A file called '$filename' already exists.\n" if -e $filename;
如果文件在過(guò)去28 天內(nèi)都未被修改,輸出警告:
warn "Config file is looking pretty old!\n" if -M CONFIG > 28;
下例首先檢查文件列表,,找到那些大于 100KB 的文件。如果一個(gè)文件僅是很大, 我們不一定會(huì)將其移到備份磁帶上去,,除非同時(shí)其在最近 90 天內(nèi)都未被訪(fǎng)問(wèn),。
my @ariginal_files = qw/ fred barney betty Wilma pebbles dino bam-bamm /; my @big_old_files; #要移到備份磁帶上的文件 foreach my $filename (@original_files){ push @big_old_files, $filename if -s $filename > 100_100 and -A $filename > 90; }
-r | 文件或目錄對(duì)此(有效的)用戶(hù)(effective user)或組是可讀的 |
-w | 文件或目錄對(duì)此(有效的)用戶(hù)或組是可寫(xiě)的 |
-x | 文件或目錄對(duì)此(有效的)用戶(hù)或組是可執(zhí)行的 |
-o | 文件或目錄由本(有效的)用戶(hù)所有 |
-R | 文件或目錄對(duì)此用戶(hù)(real user)或組是可讀的 |
-W | 文件或目錄對(duì)此用戶(hù)或組是可寫(xiě)的 |
-X | 文件或目錄對(duì)此用戶(hù)或組是可執(zhí)行的 |
-O | 文件或目錄由本用戶(hù)所有 |
-e | 文件或目錄名存在 |
-z | 文件存在,大小為 0(目錄恒為 false) |
-s | 文件或目錄存在,大小大于 0(值為文件的大小,單位:字節(jié)) |
-f | 為普通文本 |
-d | 為目錄 |
-l | 為符號(hào)鏈接 |
-S | 為 socket |
-p | 為管道(Entry is a named pipe(a "fifo")) |
-b | 為 block-special 文件(如掛載磁盤(pán)) |
-c | 為 character-special 文件(如 I/O 設(shè)備) |
-u | setuid 的文件或目錄 |
-g | setgid 的文件或目錄 |
-k | File or directory has the sticky bit set |
-t | 文件句柄為 TTY(系統(tǒng)函數(shù) isatty()的返回結(jié)果;不能對(duì)文件名使用這個(gè)測(cè)試) |
-T | 文件有些像“文本”文件 |
-B | 文件有些像“二進(jìn)制”文件 |
-M | 修改的時(shí)間(單位:天) |
-A | 訪(fǎng)問(wèn)的時(shí)間(單位:天) |
-C | 索引節(jié)點(diǎn)修改時(shí)間(單位:天) |
stat 和 lstat 函數(shù)
my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blockes) = stat($filename);
localtime 函數(shù)
my($sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst) = localtime $timestamp;
my $timestamp = 1180630098; my $date = localtime $timestamp;
gmtime
my $now = gmtime; #得到當(dāng)前的時(shí)間
位操作
10 & 12 | 按位與;當(dāng)操作數(shù)相應(yīng)位均為 1 時(shí)結(jié)果為 1 (本例結(jié)果為 8) |
10 豎線(xiàn) 12 | 按位或;當(dāng)操作數(shù)中相應(yīng)位有一個(gè)為 1 則結(jié)果為 1(本例結(jié)果為 14) |
10 ^ 12 | 按位異或;當(dāng)操作數(shù)中相應(yīng)位有且僅有一個(gè)為 1,結(jié)果才為 1(本例結(jié)果 6) |
6 << 2 | 位左移,將左邊操作數(shù)左移右邊操作數(shù)所指定的位數(shù),被移出的位置(右邊)補(bǔ) 0(結(jié)果為 24) |
25 >> 2 | 位右移,將左邊操作數(shù)右移動(dòng)右邊操作數(shù)所指定的位數(shù),丟棄多余的位數(shù)(左邊) (本例結(jié)果位 6) |
~10 | 位取反,也叫做一元位補(bǔ)運(yùn)算;其返回值為操作數(shù)中相應(yīng)位取反的值(本例為 0Xfffffff5,這和具體情況有關(guān)) |
目錄操作
在目錄樹(shù)上移動(dòng)
chdir "/etc" or die "cannot chdir to /etc: $!";
由于這是系統(tǒng)請(qǐng)求,錯(cuò)誤發(fā)生時(shí)將給變量$!賦值。通常應(yīng)當(dāng)檢查$!的值,因?yàn)樗鼘⒏嬖V你 chdir 失敗的原因。
Globbing
通常,shell 將每個(gè)命令行中的任何的文件名模式轉(zhuǎn)換成它所匹配的文件名。這被稱(chēng)作 globbing,。例如,在 echo 命令后使用了文件名模式*.pm,shell 會(huì)將它轉(zhuǎn)換成它所匹配的文件名:
$ echo *.pm barney.pm dino.pm fred.pm wilma.pm $
$ cat > show_args foreach $arg (@ARGV) { print "one arg is $arg\n"; } ^D $ perl show_args * ...
show-args 不需要知道如何進(jìn)行 globbing,這些名字已經(jīng)被處理后存在@ARGV 中了,。
在 Perl 程序中也可以使用 Glob 匹配,。
my @all_files = glob "*" ; my @pm_files = glob "*.pm";
@all_files 得到了當(dāng)前目錄下的所有文件,這些文件按照字母排序的,不包括由點(diǎn) (.)開(kāi)頭的文件。
Globbing 的替換語(yǔ)法
雖然我們?nèi)我獾氖褂?globbing 這個(gè)術(shù)語(yǔ),我們也談?wù)?glob 操作,但在許多使用 globbing 的程序中并沒(méi)有出現(xiàn) glob 這個(gè)字眼。為什么呢?原因是,許多這類(lèi)代碼在 glob 操作被命名前就寫(xiě)好了,。它使用一對(duì)尖括號(hào)(<>), 和從文件句柄讀入操作類(lèi)似:
my @all_files = <*>; # 基本上同 @all_files = glob "*" 一樣
my $dir = "/etc"; my @dir_files = <$dir/* $dir/.*>;
目錄句柄
從給定目錄得到其文件名列表的方法還可以使用目錄句柄(directory handle),。目錄句柄外形及其行為都很像文件句柄。打開(kāi)(使用 opendir 而非 open),從中讀入(使用 readdir 而非 readline),關(guān)閉(使用 closedir 而非 close),。不是讀入文件的內(nèi)容,而是將一個(gè)目錄中的文件名(以及一些其它東西)讀入,如下例:
my $dir_to_process = "/etc"; opendir DH, $dir_to_process or die "Cannot open $dir_to_process: $!"; foreach $file(readdir DH) { print "One file in $dir_to_process is $file\n"; } closedir DH;
刪除文件 unlink
unlink "slate", "bedrock", "lava";
這里有一個(gè)鮮為人知的 Unix 事實(shí),。你可以有一個(gè)文件不可讀,不可寫(xiě),不可執(zhí)行,甚至你并不擁有它,例如屬于別人的,但你仍然可以刪除它。這時(shí)因?yàn)?unlink 一個(gè)文件的權(quán)限同文件本身的權(quán)限是沒(méi)有關(guān)系的;它和目錄所包含的文件權(quán)限有關(guān)。
重命名文件
rename "over_there/some/place/some_file", "some_file";
鏈接文件
要找出符號(hào)連接指向的地方,使用 readlink 函數(shù),。它會(huì)告訴你符號(hào)連接指向的地方,如果參數(shù)不是符號(hào)連接其返回 undef:
my $where = readlink "carroll"; # 得到 "dodgson" my $perl = readlink "/usr/local/bin/perl" # 可能得到 Perl 放置的地方
創(chuàng)建和刪除目錄
mkdir "fred", 0755 or warn "Cannot make fred directory: $!"; rmdir glob "fred/*"; # 刪除 fred/下面所有的空目錄
修改權(quán)限
chmod 0755, "fred", "barney";
改變所有者
my $user = 1004; my $group = 100; chown $user, $group, glob "*.o";
如果要使用用戶(hù)名:
defined(my $user = getpwnam "merlyn") or die "user bad"; defined(my $group = getprnam "users") or die "group bad"; chown $user, $group, glob "/home/Merlyn/*";
defined 函數(shù)驗(yàn)證返回值是否為 undef, 如果請(qǐng)求的 user 或 group 不存在,則返回 undef。chown 函數(shù)返回其改變的文件的個(gè)數(shù),而錯(cuò)誤值被設(shè)置在$!之中。
改變時(shí)間戳
my $now = time; my $ago = $now -24*60*60; # 一天的秒數(shù) utime $now,$ago,glob "*" # 設(shè)成當(dāng)前訪(fǎng)問(wèn)的,一天之前修改的
字符串和排序
使用索引尋找子串
$where = index($big, $small);
Perl 查找子串第一次在大字符串中出現(xiàn)的地方,返回第一個(gè)字符的位置,。字符位置是從 0 開(kāi)始編號(hào)的,。如果子串在字符串的開(kāi)頭處找到,則 index 返回 0,。如果一個(gè)字符后,則返回1,依次類(lèi)推,。如果子串不存在,則返回-1,。
使用 substr 操作子串
$part = substr($string, $initial_position, $length);
它有三個(gè)參數(shù):一個(gè)字符串,一個(gè)從 0 開(kāi)始編號(hào)的初始位置(類(lèi)似于 index 的返回值) ,以及子串的長(zhǎng)度。返回值是一個(gè)子串:
my $mineral = substr("Fred J. Flintstone", 8, 5); # 得到 "Flint" my $rock = substr "Fred J. Flintstone", 13, 1000; # 得到 "stone" my $pebble = substr "Fred J. Flintsone", 13; # 同上,,得到 "stone" my $out = substr ("some very long string", -3, 2); # $out 得到 "in"
substr 選則的子串可以就地修改,!
#!/usr/bin/perl -w use strict; my $string = "Hello, world!"; substr($string,6) = "Jian Lee!"; print $string . "\n";
最后輸出為: “Hello,Jian Lee!”
使用 sprintf 格式化數(shù)據(jù)
sprintf 函數(shù)的參數(shù)和 printf 的參數(shù)完全相同(除了可選的文件句柄外), 但它返回的是被請(qǐng)求的字符串,而非打印出來(lái),。這對(duì)于希望將某個(gè)格式的字符串存入變量以供將來(lái)使用的情況非常方便,或者你想比 printf 提供的方法,更能控制結(jié)果:
my $data_tag = sprintf "%4d/%02d/%02d %02d:%02d:%02d", $yr, $mo, $da, $h, $m, $s;
進(jìn)程管理
system 函數(shù)
在 Perl 中調(diào)用子進(jìn)程來(lái)運(yùn)行程序的最簡(jiǎn)單方法是使用 system 函數(shù),。例如,要調(diào)用 Unix 的 date 命令,看起來(lái)如下:
system "date";
系統(tǒng)函數(shù)的參數(shù)和通常在 shell 中輸入的是一樣的。如果是復(fù)雜一些的命令,如 “ – $HOME” ,我們也只需將它們放入?yún)?shù)中:
system 'ls -l $HOME';
我們這里將雙引號(hào)變成了單引號(hào),因?yàn)?HOME 是一個(gè) shell 變量,。否則,shell 看不到美元符號(hào),因?yàn)?Perl 會(huì)將它用值進(jìn)行替換,。當(dāng)然,我們也可以這樣寫(xiě):
system "ls -l \$HOME";
exec 函數(shù)
exec 和 system 完全一樣,,除了一點(diǎn)!exec 打開(kāi)一個(gè)進(jìn)程代替當(dāng)前的 Perl 程序教程,,exec 執(zhí)行的進(jìn)程結(jié)束不再返回到perl教程。
環(huán)境變量
Perl , 環(huán)境變量可以由 %ENV 得到,。
$ENV{'PATH'} = "/home/rootbeer/bin:$ENV('PATH')"; delete $ENV{'IFS'}; my $make_result = system "make";
使用反引號(hào)得到輸出
my $now = `date`; # 捕獲 date 的輸出 print "The time is now $now"; # 已經(jīng)有換行符
像文件句柄一樣處理進(jìn)程
開(kāi)始一個(gè)并發(fā)(并行)子進(jìn)程的語(yǔ)法是將命令作為“文件名”傳給 open,并在其前面或后面加上豎線(xiàn)(|),豎線(xiàn)是“管道(pipe)”符,。基于這些原因,這通常叫做管道打開(kāi) (piped open) :
open DATE, "date|" or die "Cannot pipe from date: $!"; open MAIL, "|mail merlyn" or die "Cannot pipe to mail: $!";
fork 函數(shù)
defined (my $pid= fork) or die "Cannot fork: $!"; unless ($pid){ #子進(jìn)程在這里 exec "date"; die "cannot exec date: $!"; } #父進(jìn)程在這里 waitpid($pid, 0);
發(fā)送和接受信號(hào)
kill 2, 4201 or die "Cannot signal 4201 with SIGINT: $!";
Perl 模塊
查找
$ perldoc CGI
安裝模塊
通常模塊使用 MakeMaker ,,可以這樣安裝:
$ perl Makerfile.PL # $ perl Makerfile.PL PREFIX=/Users/fred/lib $ make install
一些模塊的創(chuàng)建者使用另一個(gè)模塊:Module::Build,來(lái)構(gòu)造及安裝他們的模塊,。此時(shí)的命令如下:
$ perl Build.PL $./Build install
使用 CPAN.pm
$ perl -MCPAN -e shell $ cpan Module::CoreList LWP CGI::Prototype
使用簡(jiǎn)單的模塊
File::Basename
use File::Basename; my $name = "/usr/local/bin/perl"; my $basename = basename $name; #得到 "perl"
僅使用模塊中的一些函數(shù)
use File::Basename qw/ basename /;
下面做法不引入任何函數(shù):
use File::Baename qw/ /; use File::Basename ( );
沒(méi)有引入任何函數(shù)時(shí),,如果要使用函數(shù),就必須用全名:
use File:Basename qw/ /; # 沒(méi)有引入函數(shù) my $betty = &dirname($wilma); # 使用我們自己的子程序 &dirname(這里沒(méi)有顯示) my $name = "/usr/local/bin/perl"; my $dirname = File::Basename::dirname $name; # 使用模塊中的 dirname
File:Spec 模塊
use File::Spec; #得到上面的$dirname, $basename 的值 my $new_name = File::Spec->catfile($dirname, $basename); rename($old_name, $new_name) or warn "Can't rename '$old_name' to '$new_name': $!";