在這個附錄中七蜘,我總結(jié)了 Perl 使用最頻繁的特殊變量沃粗,也就是系統(tǒng)預置的變量柄粹。比如$_
, $.
, S/
, $\
, $1
, $2
, $,
, $F
, @ARGV
等等喘鸟。
A.1 變量 $_
這個$_
變量,叫做Perl的缺省變量(default variable)驻右,這個變量可以說是Perl里面用的最頻繁的變量什黑。這個變量的發(fā)音是“美元下劃線(也就是dollar underline)”。
當你使用 -p 和 -n 這兩個參數(shù)的時候堪夭。輸入文件的當前行的內(nèi)容就存儲在這個變量當中愕把。同時如果你用了操作符和函數(shù)拣凹,但是沒有寫輸入的變量,那么他們默認的處理對象就是 $_
這個變量的內(nèi)容恨豁。這里有一個例子嚣镜。
perl -le '$_ = "foo"; print'
在這個例子中,我將字符串 "foo" 放在了 $_
這個變量當中橘蜜,然后使用了print
這個函數(shù)菊匿。不對這個函數(shù)設(shè)置參數(shù)的時候,print
就是打印出$_
這個變量的內(nèi)容计福,也就是字符串"foo"跌捆。
同樣的,$_
也被用在正則表達式當中象颖。請先看下面的一個例子:
perl -ne '/foo/ && print'
這個一行命令將會打印出匹配到"foo"字符串的輸入行佩厚。這個正則表達式/foo/
會隱式地作用于$_這個變量,也就是當前的行说订。
你可以寫出完整的代碼行抄瓦,但是那樣的話相比而言需要敲擊很多次的鍵盤。
perl -ne 'if ($_ =~ /foo/) { print $_ }'
如果Perl解釋器匹配到了字符串陶冷,然后就會打印出來钙姊。你也可以直接把輸入文件中所有行第一次出現(xiàn)的字符串for
給替換掉。只需要在命令中簡單地調(diào)用s/foo/bar/
:
perl -pe 's/foo/bar/'
有趣的是埃叭,Perl是從sed這個Linux命令中借鑒$_
這個變量的摸恍。還記得sed 有一個"pattern space"嗎悉罕?$_
這個變量同樣也可以叫做Perl的"pattern space"赤屋。如果你用sed來實現(xiàn)上述Perl-one-line的功能,那將會是sed 's/foo/bar/'
因為sed會把每一行放在"pattern space"當中壁袄,然后隱式地調(diào)用s
命令类早。Perl從sed當中借鑒了很多的概念和命令。
$_變量配合 -n 參數(shù)使用Perl one line
當使用 -n 參數(shù)時嗜逻,Perl 將會把你后面的代碼用如下循環(huán)包裹進你的程序涩僻。
while ( <> ){
# 你的代碼將會從這里開始執(zhí)行 (當然你需要用 -e 參數(shù)來聲明)
}
代碼中的 while ( <> ) 循環(huán) 將會從標準輸入或者文件中一行一行地讀入文本并進行處理。每一行的文本將會被放在$_
這個變量當中栈顷,然后你可以通過修改$_
變量修改這一行逆日,并打印出$_
變量的內(nèi)容。比如你可以反轉(zhuǎn)字符串并將其打印出來:
perl -lne 'print scalar reverse'
譯者注:
如果對上面的語法感覺很疑惑萄凤,可以到這個網(wǎng)站查詢其用法:https://perlmaven.com/reverse
通過這個例子我們可以看出室抽,Perl有一個獨特的語境設(shè)置(Perl有l(wèi)ist和scalar兩者語境),同一個內(nèi)容在不同的語境中有不同的效果靡努。我得知勞拉其實是一個語言學家坪圾,他加上了這些語言學的元素晓折。有人批判說這個是文字游戲,其實你結(jié)合生活一想兽泄,現(xiàn)實中又何嘗不是這樣:一個會說話的人和一個不會說話的人漓概,對同一件事情說不來的時候,給人的感覺是很不一樣的病梢,對他人的影響能力也是不一樣的胃珍。
在上面的例子中,因為我用了-n參數(shù)飘千,實際上這個程序是這樣的:
while (<>) {
print scalar reverse;
}
這個程序也等于:
while (<>) {
print scalar reverse $_
}
因為Perl的函數(shù)如果沒有參數(shù)的話堂鲜,會自動作用于$_
這個變量,所以這會造成 reverse
和reverse $_
在功能上是一樣的护奈。你需要使用scalar函數(shù)使reverse
函數(shù)發(fā)揮作用的時候在一個scalar的環(huán)境里面缔莲。否則如果不加scalar的話,那么它就在list的環(huán)境里面(因為print
函數(shù)會強制變成list的環(huán)境)霉旗,這樣就發(fā)揮不了reverse函數(shù)的作用痴奏,不信的話你可以試一試。
$_變量配合 -p 參數(shù)使用Perl one line
當你使用 -p 參數(shù)的時候厌秒,Perl 會將如下的一個循環(huán)包裹你的代碼读拆。
while (<>) {
# 你的代碼
} continue {
print or die "-p failed: $!\n";
}
譯者注:
上面的這個continue有些奇怪!
經(jīng)過不完全地測試,上述代碼等同于:
while (<>) {
# 你的代碼
print or die "-p failed: $!\n";
}
使用 -p 參數(shù)的結(jié)果幾乎與-n一模一樣鸵闪,只是使用-p參數(shù)后檐晕,每次循環(huán)后$_的內(nèi)容都會被打印出來。
如果你使用-p參數(shù)來反轉(zhuǎn)每一行的內(nèi)容的話蚌讼,你可以這樣做:
perl -pe '$_ = reverse $_'
這個語句就會變成:
while (<>) {
$_ = reverse $_;
print or die "-p failed: $!\n";
}
在這里辟灰,我修改了$_ 變量,使其等于reverse $_
篡石,這樣就使每一行都反轉(zhuǎn)了一下芥喇。
如果想了解更多關(guān)于這個參數(shù)的的用法,請看 2.1章節(jié)凰萨。
顯式使用 $_ 變量
$_經(jīng)常被顯式地使用继控,這里有一些使用的例子。
perl -le '@vals = map { $_ * 2 } 1..10; print "@vals"'
運行這個指令后輸出的結(jié)果是:2 4 6 8 10 12 14 16 18 20
在這里胖眷,我用了map
這個函數(shù)武通, 去map 一個list 中的所有元素然后返回一個新的列表,新列表中的每一個元素是map 中的表達式作用后的結(jié)果珊搀。
譯者注:這里有函數(shù)式編程的一些影子冶忱。
在這個例子中,list 是(1 .. 10) 表達式是 $_ * 2
食棕, 表達式的意思對list中的每個元素都乘以2朗和。正如你看到的一樣错沽,我直接顯式地運用了 $_
。當map
函數(shù)對list進行遍歷的時候眶拉,每個元素都很方便地可以用$_
變量來表示千埃。
那么現(xiàn)在我們學會利用便捷的map
函數(shù)來解決一些問題吧。比如對于文本的每一行都乘以2忆植。
perl -lane 'print " @{[map { $_ * 2 } @F] } " '
這個one-liner 將表達式 $_ *2
作用于@F
數(shù)組的每個元素放可。"@{[ . . .]} "這個看著很瘋狂的寫法 只是一種用來執(zhí)行 引號中代碼的一種寫法。(4.4章節(jié)可以看它的解釋)
另一個顯式地利用 $_
變量的一個函數(shù)式 grep
朝刊,這個函數(shù)能夠讓你過濾一個list中的元素耀里。這里有一個例子:
perl -le ' @vals = grep { $_ > 5} 1..10;print "@vals" '
這個語句的執(zhí)行結(jié)果是“6 7 8 9 10”。正如你看到的拾氓,grep
函數(shù)可以過濾掉1 2 3 4 5這些小于等于5的元素冯挎。
下面讓我們來應用一下grep,比如找到并且打印出當前行中所有滿足回文結(jié)構(gòu) (palindromes) 的元素咙鞍。
perl -alne 'print "@{[grep { $_ eq reverse $_ } @F]}"'
在這里grep
函數(shù)判定的條件是 $_ eq reverse $_
房官,如果當前元素滿足回文結(jié)構(gòu)則被返回,否則就被過濾掉续滋。例如翰守,如果輸入的是:
civic foo mom dad
bar baz 1234321 x
那么輸出的結(jié)果就是:
civic mom dad
1234321 x
正如你所看到的,所有的這些輸出元素都是回文結(jié)構(gòu)疲酌。
你甚至可以通過在終端鍵入perldoc perlvar
學習更多關(guān)于 $_
這個變量的知識蜡峰。
A.2 變量 $.
當Perl讀取一個文件的時候, $.
這個變量總是會包含當前讀取行的內(nèi)容朗恳。例如在下面這個例子中湿颅,我們對文件file
中的內(nèi)容標注行號。
perl -lne 'print "$. $_" file
你也可以將當前行的內(nèi)容變成原來行內(nèi)容并在每一行的末尾加上行號:
perl -pe '$_ = "$. $_"' file
當出入兩個文件的時候僻肖,變量$.
不會被重置肖爵,因此如果想對兩個文件同時標注行號卢鹦,可以直接這樣寫:
perl -pe '$_ = "$. $_"' file1 file2
這樣的話Perl會在讀完file1這個文件之后繼續(xù)標注file2這個文件臀脏,且行號是連續(xù)的。(如果file1包括了10行冀自,那么Perl讀取file2的第一行的時候揉稚,它標注的行號就是11)。
如果你想重新設(shè)定$.
這個變量熬粗,你可以顯式地關(guān)閉當前的文件句柄 ARGV
;
perl -pe '$_ = "$. $_"; close ARGV if eof' file1 file2
ARGV
是一個特殊的的文件句柄搀玖,它包含了當前打開的文件。通過呼叫 eof
驻呐,Perl就會檢查一下當前處理的行是不是文件的最后一行灌诅。如果是最后一行芳来,那close函數(shù)就會關(guān)閉它,也就是把$.
變量重置為0.
行的終止符號默認是"\n"猜拾,當然你也可以手動地通過$/
變量改變行的終止符即舌。下一節(jié)我們就來討論一下$/
這個內(nèi)置變量。
A.3 變量 $/
這個變量告訴Perl 行終止符是什么挎袜,也就是Perl認為怎么樣才叫做一行顽聂。我們再來看那個標注行號的小語句:
perl -lne 'print "$. $_"' file
Perl 讀寫文件的內(nèi)容知道讀到換行符,然后將這些內(nèi)容放入$_
這個變量盯仪。然后使$.
變量的值增加1紊搪,再進一步Perl會調(diào)用 print 函數(shù)打印出 "$. $_"的內(nèi)容,也就是打印出行號和當前行的內(nèi)容全景。
這里有另外的一個例子耀石,如果你有一個文件的內(nèi)容如下,你可以將$/
變量設(shè)置為":"爸黄,那么Perl就會一個數(shù)字一個數(shù)字地讀取娶牌,每一行就是一個數(shù)字。
3:9:0:7:1:2:4:3:8:4:1:0:0:1:...
如果你將$/
變量設(shè)置為 undef
馆纳,Perl會將整個文件視為一行放入$_
變量(這個叫做 slurping
)诗良。
perl -le '$/ = undef; open $f, "<", "file"; $contents = <$f>"
這個語句會一次性地將 file 這個文件的內(nèi)容放到$contents
這個變量中。你也可以將 $/
變量設(shè)置為一個整型數(shù)字的reference鲁驶。例如
$/ = \1024
在這個例子中鉴裹,Perl一次讀取1024字節(jié)的內(nèi)容,將其放入$_
變量钥弯。(這也叫做 record-by-record reading
)
譯者注:經(jīng)過譯者自己的調(diào)試径荔,下面的兩個結(jié)果是不一樣的。
#
perl -lpe ' BEGIN{$/ = \1}' file
# 第一次賦值`$_`的時候行分割符還是"\n"
perl -lpe ' $/ = \2;' file
你也可以用-0
參數(shù)去設(shè)置分隔符脆霎,例如 -0072 等于$/ = :
A.4 變量 $\
這個變量會在每次調(diào)用print
函數(shù)后加上這個變量的內(nèi)容总处。比如你可以在每次print后面加上"."這個內(nèi)容。
perl -e '$\ = ". "; print "hello"; print "world"'
執(zhí)行這個語句屏幕將會打印出:
hello. world.
當你需要再每個print 的內(nèi)容后面加一些東西的時候睛蛛,修飾這個變量特別有用鹦马。
請記住這個變量,例如你想在每次print 之后都加上"\n"分隔符忆肾,那么你可以在一開始就將$\
變量賦值為"\n"荸频。值得注意的是,在Perl的有些版本中有一個say
函數(shù)客冈,這個函數(shù)和print
很像旭从,但是它會在每次打印內(nèi)容后就加上"\n"。
A.5 變量 $1, $2, $3 等等
這些變量一般用來捕獲最終的匹配值,這些匹配值一般都用圓括號來捕獲和悦。例如退疫,
perl -nle 'if (/She said: (.*)/) { print $1 }'
Perl 會在當前行中匹配字符串"She said:"和后面的內(nèi)容然后捕獲所有的后面的內(nèi)容。再把這些內(nèi)容放入$1
變量鸽素,最后打印出來蹄咖。
當你再用另外一個括號的時候,后來一個捕獲的內(nèi)容就會放在$2
這個變量中付鹿。$3
也是如此澜汤。
perl -nle 'if (/(She|He) said: (.*)/) { print "$1: $2" }'
在這個語句中,首先"She"/"He"字符串將會被捕獲在$1
變量中舵匾,然后 "said: "字符串后面的內(nèi)容也會被捕獲并放在$2
這個變量當中俊抵,最后我們將這兩個變量以"$1: $2"這種形式打印出來。你使用多少個圓括號就會有多少個捕獲的變量坐梯,各個變量的名稱依次是: $1
徽诲、$2
、$3
……
注意:圓括號還有一個功能就是包裹一個連續(xù)的字符串吵血,所以有時候你不想捕獲這些內(nèi)容谎替,那怎么辦呢?你可以在圓括號中用?:
這樣的符號蹋辅。例如钱贯,可以將上面One-line語句中的(She|He)
變成(?:She|He)
:
perl -nle 'if (/(?:She|He) said: (.*)/) { print "Someone said: $1" }'
這個語句不會捕獲"She"或者"He"字符串。故第二個圓括號中捕獲的內(nèi)容就會在 $1
這個變量中侦另。
在Perl 5.10版本之后秩命,你可以用寫了名字的群(如?<name>...
)去捕獲你要的字符串。當你這樣做的時候你就不需要用$1
$2
這些變量去調(diào)用捕獲的字符串褒傅,而是用 $+{name}
去調(diào)用這些捕獲的字符串弃锐。例如,下面的這個語句可以捕獲"She"或者"He" 在 gender這個群殿托,"said:" 后面的內(nèi)容在 text 這個群霹菊。
perl -nle 'if (/(?<gender>She|He) said: (?<text>.*)/) {
print "$+{gender}: $+{text}"
}'
A.6 變量 $,
當你打印很多個值時,$,
變量是輸出域的分隔符支竹。在默認的情況下旋廷,他處于未定義狀態(tài),也就是 undefined唾戚,就是說所有輸出的變量都是緊密地被打印在一起的柳洋。例如如果你執(zhí)行下面的語句:
perl -le 'print 1, 2, 3'
你將會得到 "123" 這個輸出結(jié)果待诅,如果你將 $,
變量設(shè)置為一個分號叹坦,
perl -le '$,=":"; print 1, 2, 3'
你將會得到 "1:2:3"
A.7 變量 $"
請先看下面的兩個例子:
perl -le '@data=(1,2,3); print "@data"'
輸出的結(jié)果是 "1 2 3" 。
perl -le '@data=(1,2,3); $" = "-"; print "@data"'
輸出的結(jié)果是 "1-2-3"卑雁。
這個變量默認的值是一個空白符募书,是打印數(shù)組時的分隔符绪囱。
A.8 變量 @F
當使用 -a
參數(shù)的時候,Perl會根據(jù)空白符自動分割每一行然后將所有的元素都放在@F這個數(shù)組當中莹捡。例如輸入的行是"foo bar baz"鬼吵,然后 @F
就是一個 ("foo","bar","baz")
的數(shù)組篮赢。
這樣的技術(shù)就會允許你單獨操作每一個域(field)齿椅。 例如,你可以利用$F[2]
打印出第三個域的值:
perl -ane 'print $F[2]'
你也可以做很多次的運算启泣,比如將第五個域的值乘以2:
perl -ane '$F[4] *= 2; print "@F"'
在這里涣脚,第五個域的值乘以了2, print "@F"
語句打印出整個數(shù)組的值寥茫,用一個空格來分割遣蚀。
你可以將-a
參數(shù)和-F
參數(shù)連用,后者可以生命用哪個字符串作為輸入行的分隔符纱耻。例如去處理每行用分號分割的 /etc/passwd 文件時芭梯,你可以寫如下的語句:
perl -a -F: -ne 'print $F[0]' /etc/passwd
A.9 變量@ARGV
這個變量包含了在運行命令時,傳給Perl的參數(shù)弄喘。例如玖喘,下面的語句將會打印出 "foo bar baz" :
perl -le 'print "@ARGV"' foo bar baz
注意當你使用 -n
或者-p
參數(shù)時,Perl將會一個接一個地打開文件蘑志,然后將@ARGV
數(shù)組中的元素逐個刪去芒涡。所以如果你要通過這個數(shù)組去得到所有傳入的文件名的話,那你就要在 BEGIN{} 這個代碼塊中進行賦值:
perl -nle 'BEGIN { @A = @ARGV }; ...' file1 file2
然后你可以通過@A數(shù)組引用卖漫。
還有一個相似的變量费尽,$ARGV
,這個變量包含了當前讀取文件的文件名羊始。如果是從標準輸入讀取的旱幼,那么文件名叫做"-"。
A.10 變量 %ENV
這個哈希包含了你當前運行Shell的所有環(huán)境突委。
下面的這行語句打印出了所有的環(huán)境變量:
perl -le 'print "$_: $ENV{$_}" for keys %ENV'
這個語句循環(huán)了所有這個哈希中的所有元素柏卤,每次都將 key 放入 $_
變量然后打印出key對應的value。
·