在 Linux、BSD 或 Mac 的終端中使用 sort 命令,按自己的需求重新整理資料。
如果你曾經用過資料表應用程式,你就會知道可以按列的內容對行進行排序。例如,如果你有一個費用列表,你可能希望對它們進行按日期或價格升序抑或按類別進行排序。如果你熟悉終端的使用,你不會僅為了排序文字資料就去使用龐大的辦公軟體。這正是 sort 命令的用處。
你不必安裝 sort
,因為它向來都包含在 POSIX 系統裡。在大多數 Linux 系統中,sort
命令來自 GNU 組織打包的實用工具集合中。在其他的 POSIX 系統中,像 BSD 和 Mac,預設的 sort
命令不是 GNU 提供的,所以有一些選項可能不一樣。本文中我盡量對 GNU 和 BSD 兩者的實現都進行說明。
sort
命令預設會讀取檔案每行的第一個字元並對每行按字母升序排序後輸出。兩行中的第一個字元相同的情況下,對下一個字元進行對比。例如:
$ cat distro.listSlackwareFedoraRed Hat Enterprise LinuxUbuntuArch1337MintMageiaDebian$ sort distro.list1337ArchDebianFedoraMageiaMintRed Hat Enterprise LinuxSlackwareUbuntu
使用 sort
不會改變原檔案。sort
僅起到過濾的作用,所以如果你希望按排序後的格式儲存資料,你需要用 >
或 tee
進行重定向。
$ sort distro.list | tee distro.sorted1337ArchDebian[...]$ cat distro.sorted1337ArchDebian[...]
複雜資料集有時候不止需要對每行的第一個字元進行排序。例如,假設有一個動物列表,每個都有其種和屬,用可預見的分隔符分隔每一個“欄位”(即資料表中的“單元格”)。這類由資料表匯出的格式很常見,CSV(以逗號分隔的資料comma-separated values)字尾可以標識這些檔案(雖然 CSV 檔案不一定用逗號分隔,有分隔符的檔案也不一定用 CSV 字尾)。以下資料作為範例:
Aptenodytes;forsteri;Miller,JF;1778;EmperorPygoscelis;papua;Wagler;1832;GentooEudyptula;minor;Bonaparte;1867;Little BlueSpheniscus;demersus;Brisson;1760;AfricanMegadyptes;antipodes;Milne-Edwards;1880;Yellow-eyedEudyptes;chrysocome;Viellot;1816;Southern RockhopperTorvaldis;linux;Ewing,L;1996;Tux
對於這組範例資料,你可以用 --field-separator
(在 BSD 和 Mac 用 -t
,在 GNU 上也可以用簡寫 -t
)設定分隔符為分號(因為該範例資料中是用分號而不是逗號,理論上分隔符可以是任意字元),用 --key
(在 BSD 和 Mac 上用 -k
,在 GNU 上也可以用簡寫 -k
)選項指定哪個欄位被排序。例如,對每行第二個欄位進行排序(計數以 1 開頭而不是 0):
sort --field-separator=";" --key=2Megadyptes;antipodes;Milne-Edwards;1880;Yellow-eyedEudyptes;chrysocome;Viellot;1816;Sothern RockhopperSpheniscus;demersus;Brisson;1760;AfricanAptenodytes;forsteri;Miller,JF;1778;EmperorTorvaldis;linux;Ewing,L;1996;TuxEudyptula;minor;Bonaparte;1867;Little BluePygoscelis;papua;Wagler;1832;Gentoo
結果有點不容易讀,但是 Unix 以構造命令的管道方式而聞名,所以你可以使用 column
命令美化輸出結果。使用 GNU column
:
$ sort --field-separator=";" \\--key=2 penguins.list | column --table --separator ";"Megadyptes antipodes Milne-Edwards 1880 Yellow-eyedEudyptes chrysocome Viellot 1816 Southern RockhopperSpheniscus demersus Brisson 1760 AfricanAptenodytes forsteri Miller,JF 1778 EmperorTorvaldis linux Ewing,L 1996 TuxEudyptula minor Bonaparte 1867 Little BluePygoscelis papua Wagler 1832 Gentoo
對於初學者可能有點不好理解(但是寫起來簡單),BSD 和 Mac 上的命令選項:
$ sort -t ";" \-k2 penguins.list | column -t -s ";"Megadyptes antipodes Milne-Edwards 1880 Yellow-eyedEudyptes chrysocome Viellot 1816 Southern RockhopperSpheniscus demersus Brisson 1760 AfricanAptenodytes forsteri Miller,JF 1778 EmperorTorvaldis linux Ewing,L 1996 TuxEudyptula minor Bonaparte 1867 Little BluePygoscelis papua Wagler 1832 Gentoo
當然 -k
不一定非要設為 2
。任意存在的欄位都可以被設為排序的鍵。
你可以用 --reverse
(BSD/Mac 上用 -r
,GNU 上也可以用簡寫 -r
)選項來顛倒已經排好序的列表。
$ sort --reverse alphabet.listzyxw[...]
你也可以把輸出結果通過管道傳給命令 tac 來實現相同的效果。
理想情況下,所有人都按照 ISO 8601 標準來寫日期:年、月、日。這是一種合乎邏輯的指定精確日期的方法,也可以很容易地被計算機理解。也有很多情況下,人類用其他的方式標註日期,包括用很名字隨意的月份。
幸運的是,GNU sort
命令能識別這種寫法,並可以按月份的名稱正確排序。使用 --month-sort
(-M
)選項:
$ cat month.listNovemberOctoberSeptemberApril[...]$ sort --month-sort month.listJanuaryFebruaryMarchAprilMay[...]NovemberDecember
月份的全稱和簡寫都可以被識別。
另一個人類和計算機的常見混淆點是數位的組合。例如,人類通常把 “1024 kilobytes” 寫成 “1KB”,因為人類解析 “1 KB” 比 “1024” 要容易且更快(數位越大,這種差異越明顯)。對於計算機來說,一個 9 KB 的字串要比諸如 1 MB 的字串大(儘管 9 KB 是 1 MB 很小一部分)。GNU sort
命令提供了--human-numeric-sort
(-h
)選項來幫助正確解析這些值。
$ cat sizes.list2M12MB1k9k9007000$ sort --human-numeric-sort90070001k9k2M12MB
有一些情況例外。例如,“16000 bytes” 比 “1 KB” 大,但是 sort
識別不了。
$ cat sizes0.list2M12MB160001k$ sort -h sizes0.list160001k2M12MB
邏輯上來說,這個範例中 16000 應該寫成 16 KB,所以也不應該全部歸咎於GNU sort
。只要你確保數位的一致性,--human-numeric-sort
可以用一種計算機友好的方式解析成人類可讀的數位。
有時候工具也提供了一些與設計初衷相悖的選項。某種程度上說,sort
命令提供對一個檔案進行隨機排序的能力沒有任何意義。這個命令的工作流讓這個特性變得很方便。你可以用其他的命令,像 shuf ,或者你可以用現在的命令新增一個選項。不管你認為它是一個臃腫的還是極具創造力的使用者體驗設計,GNU sort
命令提供了對檔案進行隨機排序的功能。
最純粹的隨機排序格式選項是 --random-sort
或 -R
(不要跟 -r
混淆,-r
是 --reverse
的簡寫)。
$ sort --random-sort alphabet.listdmpa[...]
每次對檔案執行隨機排序都會有不同的結果。
GNU 和 BSD 的 sort
命令還有很多功能,所以花點時間去了解這些選項。你會驚異於 sort
的靈活性,尤其是當它和其他的 Unix 工具一起使用時。