Perl一行式:字段處理和計算

perl一行式程序系列文章Perl一行式html


獲取每行最後一個字段

$ perl -alne 'print $F[$#F]' file.log

這裏涉及到了選項"-a"、數組@F。這裏同時還會解釋-F選項,它和-a常一塊兒使用。數組

選項"-a"和awk的自動字段分割同樣,會自動將每行數據劃分爲幾個字段。劃分字段的分隔符由-F選項指定。若是沒有指定-F,則默認以空白符號進行分割(連續空格被認爲是單空格)。安全

分割後的元素全都收集到一個數組@F中,因此第一個字段的內容是$F[0],最後一個字段是$F[-1]$F[$#F]函數

若是想取多個字段,能夠對數組@F進行切片,例如第3個字段和第第5個字段@F[2,4],第3個字段到倒數第二個字段是@F[2..$#F-1]@F[1..~~@F-2]spa

獲取範圍字段

正如上面所解釋的,若是想要獲取第二個字段到倒數第二個字段:code

$ perl -lane 'print "@F[1..~~@F-2]"' file.log
$ perl -lane 'print "@F[1..$#F-1]"' file.log

指定字段分隔符

之因此單獨拿出來解釋,是由於"-F"指定分隔符時,空白符號的特殊之處。htm

對於普通字符,-F天然沒有什麼問題:blog

$ perl -F: -alne 'print $F[1]' /etc/passwd

可是想指定空白字符做爲字段的分隔符時,"-F"選項將出現故障:內存

$ perl -F" " -alne 'print $F[1]' file.log
o
a
i
y
y

發現空格分隔符根本沒起做用,而是按照NUL做爲分隔符對每一個字符都分割了。ci

這個問題在-F選項的官方手冊中已經註明了:

You can't use literal whitespace or NUL characters in the pattern

若是想要指定空白符號做爲字段分隔符,能夠考慮其它方式。例如使用\s的正則模式,或者直接不使用-F,而是直接在-e表達式中使用split函數進行行的分割。

$ perl -F'\s+' -anle 'print $F[1]' file.log
$ perl -alne 'split / /;print $F[1]' file.log

計算全部行數值的總和

假如文件內容爲:

1 3 5 9
2 3 1
10 2 3 6

想要總計全部這些數值之和,可使用以下方式:

$ perl -M'List::Util=sum' -alne '$num += sum @F;END{print $num}' file.log

或者,將全部行讀取到數組中,最後對數組加總:

$ perl -M'List::Util=sum' -alne 'push @S,@F;END{print sum @S}' file.log

這種方式對於大文件確定是不如前一種方式友好的,由於它會將全部行內容都存儲起來,而前一種方式爲全部行都只存儲一個結果$num,佔用的內存要低的多的多。

打亂字段的順序

$ perl -M'List::Util=shuffle' -alne 'print "@{[shuffle @F]}"' file.log

List::Util模塊中有一個函數shuffle,它會按照隨機的順序打亂一個列表(瞭解便可,這不是本文的重點)。例如:

$ perl -M'List::Util=shuffle' -le 'print "@{[shuffle 1..10]}"'
8 1 3 7 10 5 2 4 6 9
$ perl -M'List::Util=shuffle' -le 'print "@{[shuffle 1..10]}"'
1 2 7 10 8 4 3 5 6 9
$ perl -M'List::Util=shuffle' -le 'print "@{[shuffle 1..10]}"'
2 5 7 1 4 6 3 8 9 10

這裏我想介紹的重點是"@{[ shuffle @F ]}",它是讓一個操做能夠插入到雙引號中的方法,這個在Perl一行式必知語法基礎解釋過。

雖然目的是插入到雙引號中,但它的最終目標是爲了讓數組元素輸出時以空格分隔。因此,這種技巧不是惟一的方法,見下一小節。

指定字段輸出分隔符(無引號包圍)

上一小節經過@{[shuffle @F]}的方式能夠將打亂數組的操做插入到雙引號中。下面是其它方法。

1.指定數組的字段輸出分隔符$,

$ perl -M'List::Util=shuffle' -alne 'BEGIN{$,=" "}print shuffle @F' file.log

默認狀況下,print/say輸出列表的時候,若是數組/列表不插入到雙引號中,各元素之間緊連在一塊兒被輸出:

$ perl -le '@arr=qw(Shell Perl Python);print @arr'
ShellPerlPython

特殊變量$,指定的就是print/say輸出數組且不插入雙引號時的元素分隔符,其默認值爲undef。

例如指定爲空格:

$ perl -le '
    @arr=qw(Shell Perl Python);
    $,=" ";
    print @arr'
Shell Perl Python

2.將列表join成字符串,join的鏈接符指定爲空格便可

$ perl -M'List::Util=shuffle' -anle 'print join " ",shuffle @F' file.log

指定字段分隔符(引號包圍)

變量$,控制無雙引號包圍的數組/列表在print/say輸出時的元素分隔符。其實雙引號包圍的數組/列表被print/say輸出時也能夠指定元素分隔符。

控制輸出雙引號包圍的列表的元素分隔符的特殊變量是$",默認值爲空格。

# 默認空格分隔雙引號中的元素
$ perl -le '
    @arr = qw(Shell Perl PHP Python);
    print "@arr"'
Shell Perl PHP Python

# $"改變雙引號中的元素分隔符
$ perl -le '
    @arr = qw(Shell Perl PHP Python);
    $" = ":";
    print "@arr"'
Shell:Perl:PHP:Python

全部行中最小數值

假如文件內容爲:

1 3 0 9
2 -3 1
10 -2 -3 6

全部行的最小值爲-3,如何取得?

最簡單的方式是將全部行都讀入並保存到數組中,而後使用List::Util模塊的min函數取得。

$ perl -M'List::Util=min' -anle 'push @nums,@F;END{print min @nums}' num.log

但這對於大文件來講內存佔用率會很高。比較好的方式是從每行中取出最小值,保留到數組中,最後從這個數組中取出最小值(稍後繼續解釋更好的方式)。

$ perl -M'List::Util=min' -anle '
    push @nums,min @F;
    END{print min @nums}' num.log

若是文件的行數量很是大,這也會在內存中保留不少數值,也不是最佳方式。

更好的方式是從每行中取出最小值保存下來,而後和後面的行結合在一塊兒取最小值。這樣的方式使得整個處理過程都只佔用一行內存空間。

$ perl -M'List::Util=min' -anle '
    $min = min ($min // (),@F);
    END{print $min};' num.log

這裏的關鍵是min $min//(),@F

首先,$min//()表示若是$num未定義,則返回空列表(),不然返回$min。若是這裏不進行$min是否已定義的判斷,那麼第一次使用$min的時候,它被看成空。因此若是文件中沒有負數,下面的操做將會所以而返回空。

$ perl -M'List::Util=min' -anle '
    $min = min ($min,@F);
    END{print $min};' num.log

再者,上面$min//()$min未定義的時候返回的是空列表(),不能編寫爲返回0或空,不然就多出了一個要比較的值。

最後,min函數操做的是一個列表,而Perl會將多個列表壓扁造成一個大列表,因此$min//(),@F被壓成了一個被min函數操做的列表,而()表示空列表,這使得第一次使用$min的時候不會影響要比較的值。

將全部數值取絕對值

假如文件內容爲:

1 3 0 9
2 -3 1
10 -2 -3 6

要返回全部數值的絕對值,能夠借用默認函數abs來操做。

簡單的邏輯能夠遍歷@F,並應用abs函數,最後追加回列表被輸出:

$ perl -lane '
    @line=();
    for(@F){push @line,abs};
    print "@line";
    ' num.log

在Perl中,對於列表中每一個元素都要檢查、操做的情形,可使用map函數。map函數太強大了,堪稱逆天級,map的詳細用法參見Perl map用法詳解。這裏根據此處需求給出map的使用示例:

$ perl -lane 'print "@{[map {abs} @F]}"' num.log

其中map {op} LIST表示對LIST中的每一個元素都執行op操做,操做後的值構成一個新列表。@{[ ]}的格式在前面已經出現屢次了,再也不解釋。

統計全部匹配字段數量

$ perl -lane '/pattern/ && ++$num for @F;END{print $num || 0}' file.log

這種統計方式是安全的。先劃分字段,而後匹配每一個字段,只要匹配到就將計數器變量加1。最後輸出計數器的值。但可能匹配不到任何東西,因此必須給計數器變量設置一個默認值,也就是$num || 0

另外一種改寫方式是:

$ perl -anle '$t += /pattern/ for @F;END{print $t}' file.log

這裏採用的賦值方式$t += /pattern/,由於/pattern/返回的是匹配成功的數量,不匹配成功則會返回0,因此無需像前面同樣設置計數器變量的默認值。

若是使用grep來改寫,則一行式命令以下:

$ perl -alne '$num += grep /pattern/,@F;END{print $num}' file.log

grep返回一個新的表示能匹配的元素列表,可是在標量上下文中列表返回的是元素個數,因此可直接加總計算。

生成10個5-15之間的隨機數

$ perl -le 'print "@{[ map int(rand(10)+5),1..10 ]}"'
10 9 9 11 13 10 14 12 13 6

rand(10)表示生成0-9之間的隨機浮點數,int(rand(10))表示生成0-9之間的隨機整數,加上5表示5-14的隨機整數,整個過程執行10次,因此生成了10個隨機整數。

生成字母表

$ perl -le 'print a..z'
abcdefghijklmnopqrstuvwxyz

更準確的寫法是加上雙引號:

$ perl -le 'print "a".."z"'
abcdefghijklmnopqrstuvwxyz

逗號分隔字母表:

$ perl -le 'print join ",","a".."z"'
a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z

輸出"a"到"zz":

$ perl -le '"a".."zz"'

..符號會按照字符序列進行遞增,"z"遞增後獲得"aa",再遞增獲得"ab","az"遞增獲得"ba",依次類推。因此這裏會返回大量字符(共702個字符):

a..z
aa..az
ba..bz
...
xa..xz
ya..yz
za..zz

生成8字符隨機密碼

$ perl -le 'print map { ("a".."z")[rand 26] } 1..8'

這裏("a".."z")[rand 26]取26個字母中的一個隨機字母,1..8表示生成8個字符。

若是想要包含大小寫字母、數字,能夠:

$ perl -le 'print map { ("a".."z","A".."Z",0..9)[rand 62] } 1..8'

生成8字符隨機密碼,包含特殊字符

若是想要讓生成的隨機密碼中包含大小寫字母、數字、各類標點符號,能夠經過ascii碼錶來指定範圍,而後使用chr函數來轉換ascii碼爲字符。

根據ascii,從33開始到126結束是大小寫字母、數字、各類標點符號部分。因此:

$ perl -le 'print map { chr int(rand(94)) + 33 } 1..8'

int(rand(94) + 33)表示生成33-126(包括126)之間的隨機整數,chr函數能夠將數值轉換爲對應的字符。

$ for i in `seq 1 10`;do
    perl -le 'print map {chr int(rand(94))+33} 1..8'
done

>t]>W69#
>]e<Hub6
D}R1l)._
*(HKFZ6Q
x++\"=1O
@K)%.N@s
sSji5&FX
o+.#?@/x
^6[l~%-k
.bkTKA[%
相關文章
相關標籤/搜索