Perl一行式:選擇行輸出、刪除、追加、插入
perl一行式程式系列文章:Perl一行式
對於Perl的一行式perl程式來說,選擇要輸出的、要刪除的、要插入/追加的行是非常容易的事情,因為print/say決定行是否輸出/插入/追加/刪除。雖然簡單,但對於廣泛應用在sed的示例還是可以拿到這裡來討論一番。
因為輸出/刪除/插入/追加行都是通過print/say在不同條件下的操作,所以本文只會介紹輸出操作,刪除/插入/追加其實都是同樣的原理。
輸出第一行
$ perl -lne 'print;exit' file.log
輸出第13行
$ perl -ne 'print if $. == 13' file.log
輸出前10行
$ perl -ne 'print if $.<=10' file.log $ perl -ne 'print if 1..10' file.log $ perl -ne '$. <= 10 && print' file.log $ perl -ne 'print; exit if $. == 10' file.log
輸出最後一行
$ perl -ne '$last=$_;END{print $last}' file.log
或者通過檔案結尾eof來判斷:
$ perl -ne 'print if eof' file.log
這裡的eof函式的作用是:如果下一行讀取到了檔案尾部eof,就返回1。否則
輸出倒數10行
這個實現起來可能稍顯複雜,但邏輯很簡單:向一個數組中新增10行元素,如果陣列元素個數超過了10,則剔除陣列的第一個元素。
$ perl -ne ' push @lines,$_; if(@lines>10){ shift @lines; } END{ print @lines } ' /etc/passwd
這裡是shift一個元素來保證"視窗"的穩定性:最多隻有10個元素。另一種穩妥的方式是直接切片,從陣列中取最後10個元素:
$ perl -ne '
push @lines,$_;
@lines = @lines[@lines-10..$#lines] if @lines>10;
END{print @lines}
' /etc/passwd
輸出倒數第11行到倒數第2行
有了前一個示例作為基礎,這個需求很容易實現。
保留一個11行元素的陣列,最後輸出前10個元素即可。
$ perl -ne ' push @a,$_; shift @a if @a>11; END{print @a[0..$#a-1]} ' /etc/passwd
輸出檔案的第偶數行
這個很簡單,只需判斷行號的奇偶性即可。
$ perl -ne 'print if $. % 2 == 0' file.log
$ perl -ne 'print unless $. % 2' file.log
輸出能匹配的行
$ perl -ne 'print if /regexp/' file.log
輸出兩個匹配之間的行
$ perl -ne 'print if /regexp1/../regexp2/' file.log
輸出匹配行的前一行
只需將每行保留到變數中,如果當前行匹配了,則輸出上一行儲存的值。
$ perl -ne '/regexp/ && $last && print $last;$last = $_' file.log
如果想要輸出匹配的前M行,只需把這些數量的行儲存到陣列中,並不斷地shift剔除就可以。
輸出匹配行的後一行
$ perl -ne '$p && print; $p = /regexp/' file.log
Perl中正則表示式的匹配操作返回的是成功與否的布林真假,所以$p = /regexp/
表示如果匹配了,則$p
的值為真,否則為假。
如果$p
為真,則下一行將被輸出,且繼續對輸出行進行匹配,如果輸出行仍然能匹配,則繼續輸出下一行。
上面的過程可以改寫成邏輯更為清晰的一行式:
$ perl -ne 'if($p){print;$p=0}++$p if /regexp/' file.log
上面的$p
是一個狀態標記變數,如果匹配成功,就標記為真值,並在輸出的時候重置狀態變數。
還可以採用另一種處理邏輯:自己編寫從<>
讀取行的while迴圈,如果匹配了就繼續讀入下一行。因為讀入的下一行可能繼續匹配,所以在while迴圈中使用redo邏輯回到while迴圈的開頭。
$ perl -se '
while(<>){
if(/$reg/){
if(eof){ exit; }
print $_ = <>;
}
redo if /$reg/;
}
' -- -reg="REGEXP" file.log
輸出匹配行及其後5行
上面採用狀態標記變數$p
,這個狀態標記變數可以更深入地使用。
如果匹配了,則$p
設定為5,然後輸出後面的行時對$p
自減。
$ perl -ne '
if($p){print;$p--}
if(/regexp/){$p = 5;print};
' file.log
連續行去重
$ perl -ne '
next if "$line" eq "$_";
print $line = $_;
' file.log