perl一行式程序系列文章:Perl一行式html
假如文件file.log內容以下:express
root x 0 0 root /root /bin/bash daemon x 1 1 daemon /usr/sbin /usr/sbin/nologin bin x 2 2 bin /bin /usr/sbin/nologin sys x 3 3 sys /dev /usr/sbin/nologin sync x 4 65534 sync /bin /bin/sync
$ perl -pe '$\ = "\n"' file.log
結果:bash
root x 0 0 root /root /bin/bash daemon x 1 1 daemon /usr/sbin /usr/sbin/nologin bin x 2 2 bin /bin /usr/sbin/nologin sys x 3 3 sys /dev /usr/sbin/nologin sync x 4 65534 sync /bin /bin/sync
這裏出現了選項 -p 和 -e,出現了特殊變量$\
,附帶的,稍後還會解釋另外一個選項 -n。less
perl的"-e"選項表示後面接perl的一行式表達式,就像sed的-e選項同樣。這是一行式perl程序最多見的一個選項。perl還有一個"-E"選項,和"-e"同樣都用來指定一行式表達式,但"-E"可使用一些高版本的功能。函數
perl的"-p"選項表示print操做,即對每一讀入的行在通過表達式操做後都默認輸出,和sed的p命令是同樣的。測試
實際上,perl中的"-p"選項等價於下面的邏輯,理解了這個邏輯,對理解sed的邏輯會頗有幫助。code
while(<>){ ... -e 指定的表達式代碼在這裏 ... } continue { print or die "-p failed: $!\n"; }
Perl中的continue和其它語言的continue有點不同,Perl中的continue表示每輪循環的主體執行完以後,執行另外一段代碼。也就是說,每一行內容通過"-e"指定的表達式處理後,都會被continue代碼塊中的print輸出。htm
解釋下-p選項的過程:while(<>)
每次讀取一行數據賦值給默認變量$_
,而後通過-e的表達式進行處理,處理完後執行continue的print,這裏print沒有參數,因此表示輸出默認變量$_
的內容,也就是被處理後的行數據。對象
另外一個選項 -n ,表示處理文件但默認不輸出處理後的行。若是想要輸出,只能本身在-e表達式中指定輸出操做(print/say/printf)。邏輯爲:blog
while (<>) { ... # -e expression here }
也就是說,-n和-p兩個選項會自動讀取文件(若是都存在,則-p會覆蓋-n),不須要在-e的表達式中本身寫讀取文件的邏輯。若是沒有這兩個選項,那麼在-e中要本身寫才能讀參數文件:
$ perl -e 'while(<>){...}'
通常來講,選擇使用-p仍是-n的規則以下:
最後是關於特殊變量$\
表示print的輸出行分隔符(awk的ORS變量),它默認爲undef,因此print輸出的每段數據之間都是緊連在一塊兒的。此處示例將$\
指定爲換行符。
因爲<>
讀取數據時已經將文本中每一行的\n
也讀取了,因此加上$\
已經有兩個連續的\n
,因而每行後面都會多一空行。
實際上沒有必要爲每一讀入的行都設置$\
,能夠將它設置在BEGIN塊中:
$ perl -pe 'BEGIN{$\ = "\n"}' file.log
對於本示例"每行以後加上空行"有多種解決方式。例如:
$ perl -pe '$_ .= "\n"' file.log $ perl -nE 'say "$_\n"' file.log $ perl -pe 's/$/\n/' file.log ......
$ perl -pe '$_ .= "\n" unless /^$/'
這裏使用unless邏輯進行空行匹配,若是匹配空行,就不對當前行追加尾隨換行符。unless測試等價於if的否認測試。
有些空行多是包含了空白符號(空格、製表符等)的行,這些空白肉眼不可識別,但卻佔了字符空間,使得沒法匹配^$
,因此更好的匹配模式是:
$ perl -pe '$_ .= "\n" if /\S/'
\S
表示任意單個非空白字符,\s
表示任意單個空白字符。因此這裏的邏輯是:只要行能匹配非空白字符,就追加尾隨換行符。
想在每行後面加兩空行、三空行、四空行、N空行該如何解決?
$ perl -pe '$_ .= "\n" x 3' file.log
Perl的字符串可使用小寫字母x
表示重複N次,例如"a" x 3
獲得"aaa","ab" x 2
獲得"abab"。上面的示例表示爲每行都追加3個換行符。
經過字符串重複操做,能夠很輕鬆地輸出等長的分割線:
$ perl -e ' print "-" x 30,"\n"; print "hahaha\n"; print "-" x 30,"\n"; print "heihei\n";' ------------------------------ hahaha ------------------------------ heihei
最簡單的方式是使用s替換操做。
$ perl -pe 's/^/\n/' file.log
$ perl -ne 'print unless /^$/' file.log
此處使用了"-n"選項,表示禁止默認的輸出。print和匹配操做的對象都使用默認變量$_
。等價於:
整個邏輯是:只要匹配了空行/^$/
,就不輸出。
這也有好幾種方式能夠實現,例如:
$ perl -ne 'print if /\S/' file.log
比較獨特的一種實現方式是使用length函數:
$ perl -lne 'print if length' file.log
這裏涉及選項"-l"和函數length(),且print和length都沒有指定操做對象,因此使用默認變量$_
,等價於:
$ perl -lne 'print $_ if length $_' file.log
length函數能夠獲取字符串的字符個數,注意是字符數不是字節數。
-l選項在結合-n或-p使用的時,會自動對讀入的行移除尾隨換行符,而後在輸出的時候自動追加尾隨輸出分隔符(如換行符,如何追加分隔符請參看Perl一行式參考手冊)。
這裏的邏輯是:若是是空行,那麼在被-l移除換行符後length返回0,也就是布爾假,因此只有不是空行的行纔會被輸出。
先準備一段測試數據paragraph.log:
first paragraph: first line in 1st paragraph second line in 1st paragraph third line in 1st paragraph second paragraph: first line in 2nd paragraph second line in 2nd paragraph third line in 2nd paragraph third paragraph: first line in 3rd paragraph second line in 3rd paragraph third line in 3rd paragraph
sed/awk中想要壓縮連續空行,總要多讀入幾行進行連續空行的判斷。例如:
$ sed -nr '$!N;/^\n$/!P;D' paragraph.log
但在perl一行式中,這會變得無比的簡單:
$ perl -00pe '' paragraph.log
這裏兩個關注點:-00
和-e ''
。
-e ''
的表達式部分爲空,表示什麼也不作。什麼也不作的時候,也能夠寫成-e0
。
$ perl -00pe0 paragraph.log
-0OCTNUM
表示設置輸入行分隔符$/
。
若是省略8進制值OCTNUM,則-0表示設置$/
爲undef,即$/ = undef
,也就是一次性從文件頭讀到文件尾看成一行賦值給$_
。
這裏指定了8進制的值爲0,對應於ASCII的空字符串,即等價於$/ = ""
,它表示按段落讀取(slurp讀取模式),並壓縮連續的空行爲單個空行。
什麼是段落?中間隔了至少一空行的是上下兩個段落,段落意味着可能包含了連續的多行。可是若是隔了連續的空行呢?設置$/ = ""
會按段落讀取,並壓縮連續的空行爲單空行,而後做爲上面的段落的一部分。設置$/ = "\n\n"
也表示按段落讀取,但它不會壓縮連續的空行。
如何知道是不是按段落讀取?可用下面的示例進行測試:
$ perl -ne ' BEGIN{$/ = "";} print $_."xxxxx" if /2nd/' paragraph.log
會發現追加的幾個字符"xxxxx"是單獨附加在第二段落的尾部的,而不是能匹配"2nd"的每一行上。
在上面一節壓縮連續空行的基礎上,實現這個目的已經很是容易了:
$ perl -00pe '$_ .= "\n" x 3' paragraph.log
這表示將每一個段落之間規範爲4個連續的空行進行分隔。之因此是4空行而不是3,是由於壓縮成單空行後,又追加了3空行。
要實現這樣的功能,這個對於sed來講也很是的容易。這裏給幾個簡單示例。
1.每行單詞間的空給雙倍化:每一個空白都擴成2空格
$ perl -lpe 's/ / /g' file.log
2.移除每行單詞間的全部空白
$ perl -lpe 's/ //g' file.log
3.每行單詞間連續空白壓縮爲單空格
$ perl -lpe 's/\s+/ /g' file.log
4.全部字符間插入一個空格
$ perl -lpe 's// /g' file.log
注意,上面插入空格時,也會在行首和行尾插入空格符號。
perl的"-i"選項能夠用來原地修改、拷貝副本。用法和sed的"-i"一致。
例如:
$ perl -i".bak" -lpe 's/$/\n/g' file.log $ cat file.log root x 0 0 root /root /bin/bash daemon x 1 1 daemon /usr/sbin /usr/sbin/nologin bin x 2 2 bin /bin /usr/sbin/nologin sys x 3 3 sys /dev /usr/sbin/nologin sync x 4 65534 sync /bin /bin/sync $ cat file.log.bak root x 0 0 root /root /bin/bash daemon x 1 1 daemon /usr/sbin /usr/sbin/nologin bin x 2 2 bin /bin /usr/sbin/nologin sys x 3 3 sys /dev /usr/sbin/nologin sync x 4 65534 sync /bin /bin/sync