強大的Perl-one-liner之——Perl 的特殊變量

在這個附錄中七蜘,我總結(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ù)的話堂鲜,會自動作用于$_這個變量,所以這會造成 reversereverse $_ 在功能上是一樣的护奈。你需要使用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。

·

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匀油,一起剝皮案震驚了整個濱河市缘缚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌敌蚜,老刑警劉巖桥滨,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡齐媒,警方通過查閱死者的電腦和手機蒲每,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喻括,“玉大人邀杏,你說我怎么就攤上這事』Q” “怎么了望蜡?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拷恨。 經(jīng)常有香客問我泣特,道長,這世上最難降的妖魔是什么挑随? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任状您,我火速辦了婚禮,結(jié)果婚禮上兜挨,老公的妹妹穿的比我還像新娘膏孟。我一直安慰自己,他們只是感情好拌汇,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布柒桑。 她就那樣靜靜地躺著,像睡著了一般噪舀。 火紅的嫁衣襯著肌膚如雪魁淳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天与倡,我揣著相機與錄音界逛,去河邊找鬼。 笑死纺座,一個胖子當著我的面吹牛息拜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播净响,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼少欺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了馋贤?” 一聲冷哼從身側(cè)響起赞别,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎配乓,沒想到半個月后仿滔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惠毁,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年堤撵,在試婚紗的時候發(fā)現(xiàn)自己被綠了仁讨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羽莺。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡实昨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盐固,到底是詐尸還是另有隱情荒给,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布刁卜,位于F島的核電站志电,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蛔趴。R本人自食惡果不足惜挑辆,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望孝情。 院中可真熱鬧鱼蝉,春花似錦、人聲如沸箫荡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽羔挡。三九已至洁奈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绞灼,已是汗流浹背利术。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留低矮,地道東北人氯哮。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像商佛,于是被迫代替她去往敵國和親喉钢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容