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返回一個新的表示能匹配的元素列表,可是在標量上下文中列表返回的是元素個數,因此可直接加總計算。
$ 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
$ 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'
若是想要讓生成的隨機密碼中包含大小寫字母、數字、各類標點符號,能夠經過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[%