Linux中sort命令简介
1. 概述
对文本行进行排序是 Linux 中的一项常见任务。在本教程中,我们将通过示例学习sort命令。
2. sort命令介绍
sort 命令可以帮助我们重新排列来自标准输入 (stdin) 或文本文件的行。** sort命令 会将排序后的结果写入标准输出 (stdout)。**它在所有 Linux 发行版中都可用,因为它是GNU coreutils 包的一部分。
使用sort命令的语法 很简单:
sort [OPTION]... [FILE]...
默认情况下,排序实用程序将按字母顺序对行进行sort:
$ cat cities.txt
New York City
Paris
Beijing
Hamburg
Los Angeles
Amsterdam
$ sort cities.txt
Amsterdam
Beijing
Hamburg
Los Angeles
New York City
Paris
但是,如果我们传递不同的选项,sort命令可以做更多的事情,比如按数字排序、倒序排序、按字段排序等等。
我们将通过一些示例来学习如何使用sort命令以各种方式对行进行排序。
3. 按编号排序
很多时候,我们需要按数字对行进行排序。我们可以通过选项 -n进行 sort来做到这一点。
让我们创建一个新文件cities2.txt,并添加一个新列:population in millions。我们将按人口对新文件中的行进行排序:
$ cat cities2.txt
8.18 New York City
2.15 Paris
21.45 Beijing
1.82 Hamburg
3.90 Los Angeles
1.38 Amsterdam
$ sort -n cities2.txt
1.38 Amsterdam
1.82 Hamburg
2.15 Paris
3.90 Los Angeles
8.18 New York City
21.45 Beijing
好吧,在上面的输出中,这些行是按人口排序的。
** sort -n可以帮助我们按十进制数对行进行排序。但是,它无法正确排序带符号的二进制或十六进制数。**
4.倒序排列
要以相反的顺序对文件进行排序,我们使用选项 -r。
现在让我们按人口降序对文件cities2.txt进行排序:
$ sort -nr cities2.txt
21.45 Beijing
8.18 New York City
3.90 Los Angeles
2.15 Paris
1.82 Hamburg
1.38 Amsterdam
5. 按月份排序
有时我们的文本中有月份,例如“Nov”或“August”。** sort命令 支持方便的*-M*选项按月对行进行排序**:
$ cat months.txt
October
January
December
November
August
$ sort -M months.txt
January
August
October
November
December
6.按ASCII字符码排序
有时,我们希望按文本中的ASCII字符代码 对行进行排序。
让我们看一个文本文件:
$ cat ascii.txt
C
B
b
c
A
a
如果我们使用sort命令的默认选项对其进行排序,我们将得到:
$ sort ascii.txt
a
A
b
B
c
C
结果按字母顺序排序。
但是,它不是按 ASCII 代码顺序排列的。例如,大写字母“A”的 ASCII 码为 65,而小写字母“a”的 ASCII 码为 97。
LC_ALL是覆盖其他本地化设置的环境变量。**要改为按 ASCII 代码对行进行排序,我们必须设置环境变量LC_ALL=C,**以便我们强制按字节排序。
让我们看看这个环境变量如何改变sort命令的默认行为:
$ LC_ALL=C sort ascii.txt
A
B
C
a
b
c
在上面的命令中,我们临时设置LC_ALL=C只是为了sort命令的执行。它不会更改当前 shell 中的LC_ALL值。
7. 将排序后的输出写入文件
**默认情况下, sort命令将结果写入标准输出。*有时我们想将输出保存在文件中。我们可以将-o FILE选项传递给sort*命令以将结果写入文件而不是标准输出:
$ sort -o ascii_result.txt ascii.txt
$ cat ascii_result.txt
a
A
b
B
c
C
除了使用 -o选项,我们还可以将 stdout 重定向到我们的输出文件:
$ sort ascii.txt > ascii_result.txt
但是,如果我们想将排序后的结果写回输入文件,我们需要通过一个临时文件来完成:
$ sort ascii.txt > sorted.tmp && mv sorted.tmp ascii.txt
$ cat ascii.txt
a
A
b
B
c
C
8. 排序并删除重复项
如果我们将 -u选项传递给 sort命令,它将生成一个“唯一”的结果,输出排序的行并删除重复项:
$ cat dup.txt
New York City
Paris
Beijing
Paris
New York City
Hamburg
New York City
Hamburg
$ sort -u dup.txt
Beijing
Hamburg
New York City
Paris
9. 按键排序
到目前为止,我们总是按行首的项目排序。我们还可以按键对行进行排序。为此,我们将-k*选项传递给 sort命令。*
如果我们需要对一些基于字段的数据(例如 CSV 文件)进行排序,它会非常方便。
让我们通过 CSV 文件中的工作时间报告来了解它(姓名、月份、工作时间、评论):
$ cat working_hours.csv
Dr.Schmidt,Jan,123,some comments...
Mr.Green,Feb,20,some comments...
Dr.Schmidt,Mar,25,some comments...
Mr.Adams,Jan,77,some comments...
Mr.Green,Jan,45,some comments...
Mr.Adams,Feb,150,some comments...
Mr.Adams,Mar,80,some comments...
Mr.Green,Mar,107,some comments...
Dr.Schmidt,Feb,87,some comments...
接下来,我们将了解如何按字段和字段的一部分对 CSV 文件进行排序。
9.1. 按字段排序
比方说,我们想按第 3 个字段对行进行排序:Working Hours。
我们先看看sort命令来解决这个问题:
$ sort -t, -k 3n,3 working_hours.csv
Ms.Green,Feb,20,some comments...
Dr.Schmidt,Mar,25,some comments...
Ms.Green,Jan,45,some comments...
Mr.Adams,Jan,77,some comments...
Mr.Adams,Mar,80,some comments...
Dr.Schmidt,Feb,87,some comments...
Ms.Green,Mar,107,some comments...
Dr.Schmidt,Jan,123,some comments...
Mr.Adams,Feb,150,some comments...
现在,让我们了解命令的每个部分。
** sort命令的默认字段分隔符是空格。我们还可以使用选项*-t* 定义自定义字段分隔符。**由于 CSV 文件中的字段以逗号分隔,因此我们传递了“ -t”。
接下来,我们为*-k选项定义了一个排序键3n,3*。sort命令中key的定义是:
POS1[sorting options],POS2
POS1表示开始键位,POS2表示结束键位。如果我们不给出POS2,则该行的末尾将被视为 POS2。
我们的目标是使用*-n* (数字)选项按第 3 个字段排序,因此,我们有*-k 3n,3*。
9.2. 按字段的一部分排序
按字段排序可以帮助我们对基于字段的数据进行排序。但是,有时我们希望字段的一部分作为排序键。
现在让我们扩展上一节中的要求:我们想先按人名对working_hours.csv进行排序,然后按工作时间排序。
按工作时间排序对我们来说不是问题,但我们注意到按人名排序,我们需要从第一个字段中排除头衔( Ms. Mr. Dr. )。
我们先看看解决方案,再了解如何按字段的一部分进行排序:
$ sort -t, -k 1.4,1 -k 3n,3 working_hours.csv
Mr.Adams,Jan,77,some comments...
Mr.Adams,Mar,80,some comments...
Mr.Adams,Feb,150,some comments...
Ms.Green,Feb,20,some comments...
Ms.Green,Jan,45,some comments...
Ms.Green,Mar,107,some comments...
Dr.Schmidt,Mar,25,some comments...
Dr.Schmidt,Feb,87,some comments...
Dr.Schmidt,Jan,123,some comments...
排序键1.4,1起到了作用。让我们了解它的含义。
我们了解到排序键定义为POS1, POS2。此外,每个POS都定义为FC,因此完整的排序键定义为:
F1.C1[sorting options],F2.C2
F1 和F2代表字段索引*。在这种情况下, 第一个字段为1* 。
C1是字段F1中开始排序比较的字符索引。如果我们不定义C1,则比较从字段F1 的第一个字符开始。
C2是字段F2中结束排序比较的字符索引。如果我们省略C2,则排序比较在字段F2的最后一个字符处结束。
在我们的示例中,要从名称字段中排除标题,排序比较应从第 4 个字符开始。因此,我们有“ -k 1.4,1”。
10. 对 TSV 文件进行排序
到目前为止,我们已经学习了如何按字段对文件进行排序,并且我们一直使用 CSV 输入文件作为示例。
实际上,TSV(制表符分隔值)是另一种常用的数据格式。在本节中,让我们对 TSV 文件进行排序并回顾按字段排序的技术。
假设一些著名的电影演员聚在一起参加举重比赛,比赛结果(Name, Bodyweight KG, Score KG)记录在 TSV 文件中:
$ cat match_result.tsv
Brad Pitt 78.50 150.00
Michael Caine 77.60 149.50
Tom Hanks 79.00 148.00
Cary Grant 78.80 149.50
Spike Lee 80.00 149.50
Vin Diesel 77.89 150.00
David Tennant 79.50 147.50
Jackie Chan 78.77 151.00
Will Smith 80.50 148.00
现在,我们的任务是计算他们在比赛中的排名。根据举重规则:
- 两名玩家的分数不同:得分较高的玩家获胜。
- 两名选手得分相同:体重较轻的选手获胜。
因此,我们需要先按第三个字段降序排序,再按第二个字段升序排序。
这个问题的困难部分是按两个字段排序,但现在对我们来说不是挑战。我们可以构建按字段排序的命令:sort -k3nr,3 -k2n,2 match_result.tsv。
我们仍然需要使用*-t*选项将 Tab 指定为字段分隔符。
试一试吧:
$ sort -t'\t' -k3nr,3 -k2n,2 match_result.tsv
sort: multi-character tab ‘\\t’
糟糕, sort将“ \t ”视为多字符!接下来,让我们看看如何将 Tab 作为字段分隔符正确传递。
10.1. 传递制表符作为字段分隔符
我们有两种方法可以将 Tab 作为字段分隔符传递给sort命令:
- 传递文字标签
- 转义制表符
通常,当我们在命令行中键入TAB键时,它会触发命令完成,而不是在命令中显示文字 Tab。
但是,我们可以通过先键入CTRL-V然后键入 TAB在命令行中添加文字 Tab:
$ sort -t' ' -k3nr,3 -k2n,2 match_result.tsv
Jackie Chan 78.77 151.00
Vin Diesel 77.89 150.00
Brad Pitt 78.50 150.00
Michael Caine 77.6 149.50
Cary Grant 78.80 149.50
Spike Lee 80.00 149.50
Tom Hanks 79.00 148.00
Will Smith 80.50 148.00
David Tennant 79.50 147.50
我们应该注意到,在上面的命令中,它是*-t ‘TAB’*。
将 Tab 传递给-t*选项的另一种方法是使用*ANSI-C Quoting 转义 Tab**:
$ sort -t$'\t' -k3nr,3 -k2n,2 match_result.tsv
Jackie Chan 78.77 151.00
Vin Diesel 77.89 150.00
Brad Pitt 78.50 150.00
Michael Caine 77.60 149.50
Cary Grant 78.80 149.50
Spike Lee 80.00 149.50
Tom Hanks 79.00 148.00
Will Smith 80.50 148.00
David Tennant 79.50 147.50
伟大的!我们已经解决了这个问题。