《LINUX實操:Linux文件排序工具 sort 命令詳解》要點:
本文介紹了LINUX實操:Linux文件排序工具 sort 命令詳解,希望對您有用。如果有疑問,可以聯系我們。
本文目次:
1.1 選項闡明
1.2 sort示例
1.3 深刻研究sort
sort是排序工具,它完美貫徹了Unix哲學:"只做一件事,并做到完美".它的排序功能極強、極完整,只要文件中的數據足夠規則,它幾乎可以排出所有想要的排序成果,是一個非常優質的工具.
雖然sort很強大,但它的選項很少,使用辦法也很簡單.更讓人覺得它成功的地方在于:即使想要實現復雜、完整的sort功能,所使用的選項和一般使用時的選項沒什么不同.只不過要實現復雜功能時,必須得理解sort是如何工作的.
也便是說,沒搞懂sort工作機制時,它也能完成任務,指哪就能打哪,但沒被指到的地方難免會有所偏差和疑惑.只有搞懂了sort機制,才能真正的指哪打哪,結果中一絲偏差也沒有,即使出現了偏差也知道是為什么.
本文先解釋sort命令的常用選項,再給出sort的簡單使用示例,用于初步解釋sort各選項,最后對sort深入闡明.更完整的選項闡明可參考info sort的譯文:sort命令中文手冊(info sort翻譯).
?
sort讀取每一行輸入,并依照指定的分隔符將每一行劃分成多個字段,這些字段就是sort排序的對象.同時,sort可以指定依照何種排序規則進行排序,如依照當前字符集排序規則(這是默認排序規則)、依照字典排序規則、依照數值排序規則、依照月份排序規則、依照文件大小格式(k<M<G).還可以去除重復行,指定降序或升序(默認)的排序方式.
默認的排序規則為字符集排序規則,通常幾種常見字符的次序為:"空字符串<空白字符<數值<a<A<b<B<...<z<Z",字典排序規則也如此.
語法格局:
sort [OPTION]... [FILE]... 選項說明: -c:檢測給定的文件是否已經已經排序.如未排序,則會輸出診斷信息,提示從哪一行開始亂序. -C:類似于"-c",只不過不輸出任何診斷信息.可以通過退出狀態碼1判斷出文件未排序. -m:對給定的多個已排序文件進行合并.在合并過程中不做任何排序動作. -b:忽略字段的前導空白字符.空格數量不固定時,該選項幾乎是必須要使用的."-n"選項隱含該選項. -d:依照字典順序排序,只支持字母、數值、空白.除了特殊字符,一般情況下基本等同于默認排序規則. --debug:將顯示排序的過程以及每次排序所使用的字段、字符.同時還會在最前幾行顯示額外的信息. -f:將所有小寫字母當成大寫字母.例如,"b"和"B"是相同的. :在和"-u"選項一起使用時,如果排序字段的比較結果相等,則丟棄小寫字母行. -k:指定要排序的key,key由字段組成.key格式為"POS1[,POS2]",POS1為key起始位置,POS2為key結束位置. -n:按數值排序.空字符串""或"\0"被當作空.該選項除了能識別負號"-",其他所有非數字字符都不識別. :當按數值排序時,遇到不識別的字符時將立即結束該key的排序. -M:按字符串格式的月份排序.會自動轉換成大寫,并取縮寫值.規則:unknown<JAN<FEB<...<NOV<DEC. -o:將結果輸出到指定文件中. -r:默認是升序排序,使用該選項將得到降序排序的結果. :注意:"-r"不參與排序動作,只是操作排序完成后的結果. -s:禁止sort做"最后的排序". -t:指定字段分隔符. :對于特殊符號(如制表符),可使用類似于-t$'\t'或-t'ctrl+v,tab'(先按ctrl+v,然后按tab鍵)的方法實現. -u:只輸出重復行的第一行.結合"-f"使用時,重復的小寫行被丟棄.?
此小節為sort的簡單用法示例,也是平時最可能用上的示例.如果只是為了使用sort,而不是為了刨根問題,本小節已經足夠.
假設當前已有文件system.txt,內容如下:其中空白部門為單個制表符.
(1).不加任何選項時,將對整行從第一個字符開始依次向后直到行尾依照默認的字符集排序規則做升序排序.
[root@linuxidc tmp]# sort system.txt 1 mac 2000 500 2 winxp 4000 300 3 bsd 1000 600 4 linux 1000 200 5 SUSE 4000 300 6 Debian 600 200
由于每行的第一個字符1<2<3<4<5<6,所以成果如上.
(2).以第三列為排序列進行排序.由于要劃分字段,以是指定字段分隔符.指定制表符這種無法直接輸入的特殊字符的方式是$'\t'.
[root@linuxidc tmp]# sort -t $'\t' -k3 system.txt 4 linux 1000 200 3 bsd 1000 600 1 mac 2000 500 2 winxp 4000 300 5 SUSE 4000 300 6 Debian 600 200
結果中雖然1000<2000<4000的順序是對了,但600卻排在最后面,因為這是依照默認字符集排序規則進行排序的,字符6大于4,所以排最后一行.
(3).對第三列按數值排序規矩進行排序.
[root@linuxidc tmp]# sort -t $'\t' -k3 -n system.txt 6 Debian 600 200 3 bsd 1000 600 4 linux 1000 200 1 mac 2000 500 2 winxp 4000 300 5 SUSE 4000 300
成果中600已經排在第一行.成果中第2行、第3行的第三列值均為1000,如何決定這兩行的順序?
(4).在對第3列按數值排序規則排序的根基上,使用第四列作為決勝屬性,且是以數值排序規則對第四列排序.
[root@linuxidc tmp]# sort -t $'\t' -k3 -k4 -n system.txt 6 Debian 600 200 4 linux 1000 200 3 bsd 1000 600 1 mac 2000 500 2 winxp 4000 300 5 SUSE 4000 300
如果想在第3列按數值排序后,以第2列作為決勝列呢?由于第2列為字母而非數值,所以下面的語句是差錯的,雖然得到了期望的結果.
[root@linuxidc tmp]# sort -t $'\t' -k3 -k2 -n system.txt 6 Debian 600 200 3 bsd 1000 600 4 linux 1000 200 1 mac 2000 500 2 winxp 4000 300 5 SUSE 4000 300
之所以最終得到了正確的結果,是因為默認情況下,在命令行中指定的排序行為結束后,sort還會做最后一次排序,這最后一次排序是對整行依照完全默認規則進行排序的,也就是按字符集、升序排序.由于1000所在的兩行中的第一個字符3小于4,所以3排在前面.
之所以說上面的語句是錯誤的,是因為第2列第一個字符是字母而不是數值,在按數值排序時,字母是不可辨認字符,一遇到不可辨認字符就會立即結束該字段的排序行為.可以使用"--debug"選項來查看排序的過程和排序時所使用的列.注意,該選項只有CentOS 7上的sort才有.
[root@linuxidc tmp]# sort --debug -t $'\t' -k3 -k2 -n system.txt sort: using ‘en_US.UTF-8’ sorting rules sort: key 1 is numeric and spans multiple fields sort: key 2 is numeric and spans multiple fields 6>Debian>600>200 ___ # 第1次排序行為,即對"-k3"排序,此次用于排序的字段為第3列 ^ no match for key # 第2次排序行為,即對"-k2"排序,但顯示無法匹配排序key ________________ # 默認sort總會進行最后一次排序,排序對象為整行 3>bsd>1000>600 ____ ^ no match for key ______________ 4>linux>1000>200 ____ ^ no match for key ________________ 1>mac>2000>500 ____ ^ no match for key ______________ 2>winxp>4000>300 ____ ^ no match for key ________________ 5>SUSE>4000>300 ____ ^ no match for key _______________
(5).在對第3列按數值排序規則排序的根基上,使用第2列作為決勝屬性,且以默認排序規則對此列降序排序.
[root@linuxidc tmp]# sort -t $'\t' -k3n -k2r system.txt 6 Debian 600 200 4 linux 1000 200 3 bsd 1000 600 1 mac 2000 500 2 winxp 4000 300 5 SUSE 4000 300
由于既要對第3列按數值升序排序,又要對第2列按默認規則降序排序,因此只能對每個字段單獨分配選項.注意,雖然"r"選項是降序結果,但它不影響排序過程,只影響最終排序結果.也就是說,在依照升序排序結束得到最終結果后,再反轉第2列順序,也就是得到了降序的結果.同樣也說明,sort在排序的時候,一定且只能依照升序排序,只有排序動作結束了"r"選項才開始工作.
緊跟在字段后的選項(如"-k3n"的"n"和"-k2r"的"r")稱為私有選項,使用短橫線寫在字段外的選項(如"-n"、"-r")為全局選項.當沒有為字段分配私有選項時,該排序字段將繼承全局選項.當然,只有像"-n"、"-r"這樣的排序性的選項能力繼承和分配給字段,"-t"這樣的選項則無法分配.
是以,"-n -k3 -k4"、"-n -k3n -k4"和"-k3n -k4n"是等價的,"-r -k3n -k4"和"-k3nr -k4r"是等價的.
實際上,上面的命令寫法并不嚴謹.更尺度的寫法應該如下:
sort -t $'\t' -k3n -k2,2r system.txt
"-k2,2"表示排序對象從第2個字段開始到第2個字段結束,也便是限定了只對第二個字段排序.它的格式為"POS1,POS2",如果省略POS2,將自動擴展到行尾,即"-k2"等價于"-k2,4",也便是說,對整個第2列到第4列進行排序.
需要注意,由于上面的"-k2"繼承了全局默認的排序規則,即按字符排序而非按數值排序,此時它能夠等價于"-k2,4",但如果是"-k2n"依照數值排序的話,它不等價于"-k2,4n"或"-k2n,4n"或"-k2n,4"(這3者為等價寫法),之所以不等價,是因為按數值排序時只能識別數字和負號"-",當排序時遇到其他所有字符,都將立即結束此次排序.所以"-k2n"等價于"-k2,2n"或"-k2n,2"或"-k2n,2n".
這些理論性的知識點,請參照下一小節sort的理論內容.后文也不再解釋理論性的內容,只是介紹命令使用辦法.
(6).在對第3列按數值排序規則排序的根基上,使用第2列的第2個字符作為決勝屬性,且以默認排序規則對此列升序排序.
[root@linuxidc tmp]# sort -t $'\t' -k3n -k2.2,2.2 system.txt 6 Debian 600 200 4 linux 1000 200 3 bsd 1000 600 1 mac 2000 500 2 winxp 4000 300 5 SUSE 4000 300
其中"-k2.2,2.2"表示從第2個字段的第2個字符開始,到第2個字段的第2個字符結束,即嚴格限定為第2個字段第2個字符.如果必要對此字符降序排序,則"-k2.2,2.2r".
(7).使用"-u"去除重復字段所在的行.例如第3列有兩行1000,兩行4000,去除字段重復的行時,將只保存排在前面的第一行.
[root@linuxidc tmp]# sort -t $'\t' -k3n -u system.txt 6 Debian 600 200 3 bsd 1000 600 1 mac 2000 500 2 winxp 4000 300
由于必要去除重復字段的行,因此使用"-u"時將禁止sort做"最后一次排序".至于字段重復的行中,如何判斷哪一行是排在最前面的行,必要搞懂sort的整個工作機制,請通讀本文.
"sort -u"和"sort | uniq"是等價的,但是如果多指定幾個選項,它們將不等價.例如,"sort -n -u"只會檢查排序字段數值部門的唯一性,但"sort -n | uniq"在sort對行中字段按數值排序后,uniq將檢查整個行的唯一性.
(8).將排序結果保存到文件中.即可以使用重定向,也可以使用"-o"選項,但使用重定向不可保存到原文件,因為在sort開始執行前,原文件先被重定向截斷.而使用"-o"則沒有這樣的問題,因為sort在打開文件前先完成數據的讀取.但"-o"和"-m"一起使用時,同樣不平安.
[root@linuxidc tmp]# sort -t $'\t' -k3n -o system1.txt system.txt
(9).使用"-c"或"-C"檢測文件是否排過序.如果已排序,則不返回任何信息,退出狀態碼為0.如果未排序,退出狀態碼為1,但"-c"會給出診斷信息,并指明從哪一行開端亂序,而"-C"不返回任何信息.
[root@linuxidc tmp]# sort -c -k3n system.txt ;echo $? sort: system.txt:3: disorder: 3 bsd 1000 600 1
闡明system.txt中的第3行開始出現亂序,且退出狀態碼為1.
[root@linuxidc tmp]# sort -C -k3n system.txt ;echo $? 1
咋一看上去,sort的使用辦法很簡單,不就是"sort -t DELIMITER -k POS1,POS2 file"嗎,確實如此,它的man文檔也才100來行,連info文檔加上一堆廢話也才500多行.但事實上,sort命令很難,也可以說很簡單,簡單是因為不管是復雜功能還是簡單功能,用來用去就那么幾個選項,難是因為沒搞懂它的工作機制和細節時,有些時候的結果會比較出人意料,也不知道為什么會如此.
本小節主要講理論和工作機制的細節,偶爾給出幾個示例,所以遇到疑惑時請自行測試,當然也歡迎在博客下方留言.另外,"--debug"(CentOS7才支持該選項)選項對排疑解惑有極大贊助,所以應該善用該選項.
(1).sort命令默認依照字符集的排序規則進行排序,可以指定"-d"選項依照字典順序排序,指定"-n"依照數值排序,指定"-M"依照字符格式的月份規則排序,指定"-h"依照文件容量大小規則排序.
字符集排序規則和字典排序規則對能辨認的字符來說,順序一般是一致的,幾種常見字符的順序為:"空字符串<空白字符<數值<a<A<b<B<...<z<Z".
指定不同的排序規則,不僅改變排序時的依據,還間接影響排序時的行為,因為不同排序規則能夠辨認的字符類型不同.至于如何影響,見下面的(4).
(2).sort使用"-t"選項指定的分隔符對每行進行分割,獲得多個字段,分隔符不作為字段的內容.默認的分隔符為空白字符和非空白字符之間的空字符,并非網上眾多文章所說的空格或制表符(原文:By default, fields are separated by the empty string between a non-blank character and a blank character.).
例如," foo bar"默認將分隔為兩個字段" foo"和" bar",而使用空格作為分隔符時將分隔為三個字段:第一個字段為空,第二個字段和第三個字段為"foo"和"bar".使用下面三個sort語句可以驗證默認的分隔符并非空格.
[root@linuxidc ~]# echo -e " 234 bar\n 123 car" | sort -t ' ' -b -k3 234 bar 123 car [root@linuxidc ~]# echo -e " 234 bar\n 123 car" | sort -b -k2 234 bar 123 car [root@linuxidc ~]# echo -e " 234 bar\n 123 car" | sort -b -k3 # -k3指定的字段超越了范圍,所以key為空 123 car 234 bar
(3).使用"-k"選項指定排序的key.不指定排序key時,整行將成為排序key,即對整行進行排序.
以下是幾個例子:例子中出現了選項"n"的,描述暫不嚴謹,但目前只能如此描述,在稍后的(4)中解釋.
?"-k 2":?因為沒有指定POS2,所以key擴展到了行尾.因此該key從第2字段第一個字符開始,到行尾停止.
?"-k 2,3"?:該key從第2字段第一個字符開始到第3字段最后一個字符停止.
?"-k 2,2":?該key僅擁有第2字段.
?"-k 2,3n"和"-k 2n,3"和"-k 2n,3n"?:這三者等價,因為除了"b"選項,OPTS指定在POS1或POS2的成果是一樣的.
?"-k 2,3b"和"-k 2b,3"和"-k 2b,3b"?:這三者互不等價.
?"-k 2n":?該key從第2字段開端直到行尾,都按數值排序.
?"-k 2.2b,3.2n":?該key從第2字段的第2個非空白字符開始,到第3字段第2字符(可能包含空白字符)結束,且該key依照數值排序.其實此處的b選項是多余的,因為n隱含了b選項.
?"-k 5b,5 -k 3,3n":?定義了兩個排序key,主排序key為第5字段不包含空白字符的部分,副key為第三個字段.主key依照默認規則排序,副key依照數值排序.副key在主key排序后的基礎上再排序.
?"-k 5,5n -k 3b,6b":?主key為第5字段,依照數值排序,副key從第3字段到第六字段,忽略前導空白字符,但是依照默認規則排序.副key在主key排序后的基礎上再排序.
(4).當排序規則選項(例如"n"、"d"、"M"、"h")發現不識別的符號時,將立即結束當前key的排序.默認排序規則是字符集的排序規則,通常能識別所有字符,所以總會對整個key進行完整的排序.這是"何時跨字段、跨key比擬?"的問題.
例如,指定n選項按數值排序時,由于"n"選項只能識別數字和負號"-",當排序時遇到無法識別字符時,將導致該key的排序立即結束.也就是說,對于"abc 123 456 abc"這樣的輸入,分隔符為空格,當指定"-k 2,3n"時,雖然排序key包含"123 456",但由于中間的空白字符無法被n識別,使得在排完第2字段"123"時就立即結束該key的排序.
正因如此,使得n選項絕對不會跨字段、跨key進行比較.因此,"-k 2,3n"和"-k 2n"、"-k 2,2n"、"-k 2,4n"的結果是等價的,都只對第2字段依照數值進行排序.但默認的排序規則不會有這樣的問題,因為默認排序規則能識別所有字符,也就是說"-k 2,3"、"-k 2"、"-k 2,2"、"-k 2,4"是互不等價的.
同理,"-d"的字典排序規則只能辨認字母、數字和空白字符,所以遇到非這3類字符時也將立即結束當前key的排序."-h"和"-M"也都有字符的辨認限制,處理方式也一樣.關于"-h"和"-M"選項的說明,見info sort.
必要特意說明的是:n同樣不識別空字符串,發現空字符串時也結束排序.這可能會適得按數值排序的結果出人意料.例如:
[root@linuxidc ~]# echo -e "b 100:200 200\na 110 300" | tr ':' '\0'|sort -t ' ' -k2n b 100200 200 a 110 300
對于"b 100\0200 200"這樣的行,"-k 2n"使得該key為"100\0200".雖然結果看上去是100200,但卻只對100進行排序,也便是說它小于110.這就造成了數值排序的假象,100200竟然比110小.
(5).默認環境下,sort會進行一次"最后的排序".使用"-s"選項將禁止"最后的排序","-u"選項隱含"-s"選項.
考慮這樣一種情況:兩行在所有key的排序成果上都完全相同,應該如何決定這兩行的先后順序?
例如:
[root@linuxidc ~]# echo -e "b 100 200\na 100 300" | sort -t ' ' -k2n a 100 300 b 100 200
第一行為"b 100 200",第二行為"a 100 300".由于第2字段都是100,所以這兩行在該key上的數值排序的結果相同,于是sort采取最后的手段,完全依照默認規則(即按字符集排序規則升序排序)對整行進行一次排序,這次排序稱為"最后的排序"(info sort中稱為last-resort comparison).由于最后的排序過程中,第一個字符a<b,所以最終結果將是第二行"a 100 300"在第一行"b 100 200"的前面.
禁止"最后的排序"后,對那些排序key相同的行,將保存被讀取時相對順序.即,先讀取的排在前面.
如果上面的例子中,第二字段不采納數值排序,而是默認排序規則排序呢?如下:
[root@linuxidc ~]# echo -e "b 100 200\na 100 300" | sort -t ' ' -k2 b 100 200 a 100 300
由于默認的排序規則是依照字符集排序規則進行排序,它能識別所有的字符,所以會對"-k2"整個key進行排序,該key會自動擴展為第2字段和第3字段,由于第三字段的2小于3,所以結果中第一行排在第二行的前面.即使如此,sort還是進行了"最后的排序",只不過"最后的排序"不影響排序結果.
如果未指定任何排序選項,其自己就是完全默認的,因此沒必要再做最后的排序,所以將不會進行"最后的排序".如果指定的是"-r"選項,由于"-r"是對最終結果進行反轉排序,因此會影響這次的"最后的排序"的結果.
(6).sort的使用建議.
搞清楚了以上幾點,是否感覺sort能實現幾乎所有的排序需求呢?只要文件夠規則,sort就能控制任何一列或多列的排序方式,而且可以設置出是否跨列、跨字符、跨key排序.
這里有幾個sort使用建議,算是最后的彌補.
最后,給出一個測試題:假設一些待排序的日志文件中的內容格式如下:
4.150.156.3 - - [01/Apr/2004:06:31:51 +0000] message 1
211.24.3.231 - - [24/Apr/2004:20:17:39 +0000] message 2
能否懂得下面兩條等價的命令?
sort -s -t ' ' -k 4.9n -k 4.5M -k 4.2n -k 4.14,4.21 file*.log | sort -s -t '.' -k 1,1n -k 2,2n -k 3,3n -k 4,4n sort -s -t ' ' -k 4.9n -k 4.5M -k 4.2n -k 4.14,4.21 file*.log | sort -s -t '.' -n -k1 -k2 -k3 -k4
本文永遠更新鏈接地址:
學習更多LINUX教程,請查看站內專欄,如果有LINUX疑問,可以加QQ交流《LINUX實操:Linux文件排序工具 sort 命令詳解》。
轉載請注明本頁網址:
http://www.snjht.com/jiaocheng/8852.html