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的简单用法示例,也是平时最可能用上的示例。如果只是为了使用sort,而不是为了刨根问题,本小节已经足够。

假设当前已有文件system.txt,内容如下:其中空白部分为单个制表符。

[root@localhost ~]# vi 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).不加任何选项时,将对整行从第一个字符开始依次向后直到行尾按照默认的字符集排序规则做升序排序。

[root@localhost ~]# 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@localhost ~]# sort -t $'\t' -k3 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

结果中虽然1000<2000<4000的顺序是对了,但600却排在最后面,因为这是按照默认字符集排序规则进行排序的,字符6大于4,所以排最后一行。

[root@localhost ~]# last|grep '192'|awk '{S[$3]++} END{for(a in S) {print a,S[a]}}'|sort  -rn
192.168.1.5 6
192.168.1.12 1

 

[root@localhost files]# vi testsort.txt 
google 110 5000
baidu 100 5000
guge 50 3000
sohu 100 4500
~

让这个文件按公司的字母顺序排序,也就是按第一个域进行排序:

[root@localhost files]# sort -t ' ' -k 1 testsort.txt
baidu 100 5000
google 110 5000
guge 50 3000
sohu 100 4500

 

 

[root@master ~]# cat seq.txt 
banana
apple
pear
orange
[root@master ~]# sort seq.txt

apple
banana
orange
pear
复制代码
[root@master ~]# sort -r number.txt     #降序排序
9
8
7
6
5
4
3
2
1
[root@master ~]# cat number.txt 
9
8
7
6
5
4
3
2
1    
复制代码
复制代码
[root@master ~]# sort  number.txt   升序排序。
1
2
3
4
5
6
7
8
9
复制代码
复制代码
[root@master ~]# sort -u -n  number.txt    去除重复的数值
1
2
3
4
5
6
7
8
9
复制代码
由于sort默认是把结果输出到标准输出,所以需要用重定向才能将结果写入文件,形如sort filename > newfile。
但是,如果你想把排序结果输出到原文件中,用重定向可就不行了。

 

复制代码
[root@master ~]# sort -r number.txt -o number1.txt
[root@master ~]# cat number1.txt 
9
8
7
6
5
4
3
2
1
复制代码
你有没有遇到过10比2小的情况。我反正遇到过。出现这种情况是由于排序程序将这些数字按字符来排序了,排序程序会先比较1和2,显然1小,所以就将10放在2前面喽。这也是sort的一贯作风。
我们如果想改变这种现状,就要使用-n选项,来告诉sort,“要以数值来排序”!
[root@localhost sh2]# sort number.txt                34比4小,
复制代码
[root@master ~]# sort number.txt
加上-n以数值排序
1
12
2
23
3
4
45
5
6
65
7
76
8
89
9
复制代码
复制代码
[root@master ~]# sort -n number.txt     加上-n以数值排序
1
2
3
4
5
6
7
8
9
12
23
45
65
76
89
复制代码
[root@master ~]# vi facebook.txt

banana:30:5.5
apple:10:2.5
pear:90:2.3
orange:20:3.4
~
这个文件有三列,列与列之间用冒号隔开了,第一列表示水果类型,第二列表示水果数量,第三列表示水果价格。
那么我想以水果数量来排序,也就是以第二列来排序,如何利用sort实现?
幸好,sort提供了-t选项,后面可以设定间隔符。(是不是想起了cut和paste的-d选项,共鸣~~)
指定了间隔符之后,就可以用-k来指定列数了。
[root@master ~]# sort -n -k 2 -t : facebook.txt   以冒号为分隔符,第二列数值以升序排序。
apple:10:2.5
orange:20:3.4
banana:30:5.5
pear:90:2.3
[root@master ~]# sort -n -r -k 2 -t : facebook.txt    第二列降序排序。
pear:90:2.3
banana:30:5.5
orange:20:3.4
apple:10:2.5

其他的sort常用选项

-f会将小写字母都转换为大写字母来进行比较,亦即忽略大小写
-c会检查文件是否已排好序,如果乱序,则输出第一个乱序的行的相关信息,最后返回1
-C会检查文件是否已排好序,如果乱序,不输出内容,仅返回1
-M会以月份来排序,比如JAN小于FEB等等
-b会忽略每一行前面的所有空白部分,从第一个可见字符开始比较。
有时候学习脚本,你会发现sort命令后面跟了一堆类似-k1,2,或者-k1.2 -k3.4的东东,有些匪夷所思。今天,我们就来搞定它—-k选项!
我想让这个文件按公司的字母顺序排序,也就是按第一个域进行排序:(这个facebook1.txt文件有三个域)
[root@master ~]# vi facebook1.txt

baidu 100 5000
google 110 5000
guge 50 3000
sohu 100 4500

看到了吧,就直接用-k 1设定就可以了。(其实此处并不严格,稍后你就会知道)

[root@master ~]# sort -n -t ' ' -k 2 facebook1.txt 
guge 50 3000
baidu 100 5000
sohu 100 4500
google 110 5000
[root@master ~]# sort -n -t ' ' -k 2 -k 3 facebook1.txt
guge 50 3000
sohu 100 4500
baidu 100 5000
google 110 5000
看,我们加了一个-k2 -k3就解决了问题。对滴,sort支持这种设定,就是说设定域排序的优先级,先以第2个域进行排序,如果相同,再以第3个域进行排序。(如果你愿意,可以一直这么写下去,设定很多个排序优先级)
我想让facebook1.txt按照员工工资降序排序,如果员工人数相同的,则按照公司人数升序排序:(这个有点难度喽)
[root@master ~]# sort -n -t ' ' -k 3r -k 2 facebook1.txt   
baidu 100 5000
google 110 5000
sohu 100 4500
guge 50 3000

此处有使用了一些小技巧,你仔细看看,在-k 3后面偷偷加上了一个小写字母r。你想想,再结合我们上一篇文章,能得到答案么?揭晓:r和-r选项的作用是一样的,就是表示逆序。因为sort默认是按照升序排序的,所以此处需要加上r表示第三个域(员工平均工资)是按照降序排序。此处你还可以加上n,就表示对这个域进行排序时,要按照数值大小进行排序,举个例子吧 

[root@master ~]# sort -t ' ' -k 3nr -k 2n facebook1.txt 
baidu 100 5000
google 110 5000
sohu 100 4500
guge 50 3000

突发奇想,从公司英文名称的第二个字母开始进行排序:

[root@master ~]# sort -t ' ' -k 1.2 facebook1.txt  
baidu 100 5000
sohu 100 4500
google 110 5000
guge 50 3000
看,我们使用了-k 1.2,这就表示对第一个域的第二个字符开始到本域的最后一个字符为止的字符串进行排序。你会发现baidu因为第二个字母是a而名列榜首。sohu和 google第二个字符都是o,但sohu的h在google的o前面,所以两者分别排在第二和第三。guge只能屈居第四了。
又突发奇想,,只针对公司英文名称的第二个字母进行排序,如果相同的按照员工工资进行降序排序:
[root@master ~]# sort -t ' ' -k 1.2,1.2 -k 3,3nr facebook1.txt   
baidu 100 5000
google 110 5000
sohu 100 4500
guge 50 3000
由于只对第二个字母进行排序,所以我们使用了-k 1.2,1.2的表示方式,表示我们“只”对第二个字母进行排序。(如果你问“我使用-k 1.2怎么不行?”,当然不行,因为你省略了End部分,这就意味着你将对从第二个字母起到本域最后一个字符为止的字符串进行排序)。对于员工工资进行排 序,我们也使用了-k 3,3,这是最准确的表述,表示我们“只”对本域进行排序,因为如果你省略了后面的3,就变成了我们“对第3个域开始到最后一个域位置的内容进行排序” 了。
 在modifier部分还可以用到哪些选项?
可以用到b、d、f、i、n 或 r。
其中n和r你肯定已经很熟悉了。
b表示忽略本域的签到空白符号。
d表示对本域按照字典顺序排序(即,只考虑空白和字母)。
f表示对本域忽略大小写进行排序。
i表示忽略“不可打印字符”,只针对可打印字符进行排序。(有些ASCII就是不可打印字符,比如\a是报警,\b是退格,\n是换行,\r是回车等等)
思考思考关于-k和-u联合使用的例子:
[root@master ~]# sort -n -k 2 -u facebook1.txt
guge 50 3000
baidu 100 5000
google 110 5000

当设定以公司员工域进行数值排序,然后加-u后,sohu一行就被删除了!原来-u只识别用-k设定的域,发现相同,就将后续相同的行都删除。

[root@master ~]# sort -k 1 -u facebook1.txt  
baidu 100 5000
google 110 5000
guge 50 3000
sohu 100 4500
[root@master ~]# sort -k 1.1,1.1 -u facebook1.txt   
baidu 100 5000
google 110 5000
sohu 100 4500

这个例子也同理,开头字符是g的guge就没有幸免于难。

[root@master ~]# sort -k 2 -k 3 -u facebook1.txt
sohu 100 4500
baidu 100 5000
google 110 5000
guge 50 3000

咦!这里设置了两层排序优先级的情况下,使用-u就没有删除任何行。原来-u是会权衡所有-k选项,将都相同的才会删除,只要其中有一级不同都不会轻易删除的:)(不信,你可以自己加一行sina 100 4500试试看)

[root@master ~]# vi facebook1.txt

baidu 100 5000
google 110 5000
guge 50 3000
sohu 100 4500
sina 100 4500
[root@master ~]# sort -n -k 2 -k 3 -u facebook1.txt
guge 50 3000
sohu 100 4500
baidu 100 5000
google 110 5000

最诡异的排序:

[root@master ~]# sort -n -k 2.2 -k 3.1 facebook1.txt     升序
guge 50 3000
sina 100 4500
sohu 100 4500
baidu 100 5000
google 110 5000
[root@master ~]# sort -n -k  2.2 -k 3.1 -r facebook1.txt   降序。
google 110 5000
baidu 100 5000
sohu 100 4500
sina 100 4500
guge 50 3000
以第二个域的第二个字符开始到第三个域的第一个字符结束的部分进行排序。
第一行,会提取0 3,第二行提取00 5,第三行提取00 4,第四行提取10 5。
又因为sort认为0小于00小于000小于0000….
因此0 3肯定是在第一个。10 5肯定是在最后一个。但为什么00 5却在00 4前面呢?(你可以自己做实验思考一下。)
答案揭晓:原来“跨域的设定是个假象”,sort只会比较第二个域的第二个字符到第二个域的最后一个字符的部分,而不会把第三个域的开头字符纳入比较范围。当发现00和00相同时,sort就会自动比较第一个域去了。当然baidu在sohu前面了。用一个范例即可证实:
[root@master ~]# sort -n -k  2.2 -k 3.1 -k 1r facebook1.txt  
guge 50 3000
sohu 100 4500
sina 100 4500
baidu 100 5000
google 110 5000
有时候在sort命令后会看到+1 -2这些符号,这是什么东东?
关于这种语法,最新的sort是这么进行解释的:
On older systems, `sort’ supports an obsolete origin-zero syntax `+POS1 [-POS2]‘ for specifying sort keys. POSIX 1003.1-2001 (*note Standards conformance::) does not allow this; use `-k’ instead.
原来,这种古老的表示方式已经被淘汰了,以后可以理直气壮的鄙视使用这种表示方法的脚本喽!
(为了防止古老脚本的存在,在这再说一下这种表示方法,加号表示Start部分,减号表示End部分。最最重要的一点是,这种方式方法是从0开始计数的,以前所说的第一个域,在此被表示为第0个域。以前的第2个字符,在此表示为第1个字符。明白?)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @   星火撩原  阅读(278)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示

目录导航