sort命令使用说明
1、命令概述
sort命令是在Linux里非常有用,它将文件进行排序,并将排序结果标准输出。sort命令既可以从特定的文件,也可以从stdin中获取输入。
2、命令语法
sort 【选项】 【文件】
3、命令选项
-b:忽略每行前面开始出的空格字符
-c:检查文件是否已经按照顺序排序
-d:排序时,处理英文字母、数字及空格字符外,忽略其他的字符
-f:排序时,将小写字母视为大写字母(忽略大小写)
-g:按通用数值排序,支持科学计数法
-i:排序时,除了040至176之间的ASCII字符外,忽略其他的字符
-m:将几个排序号的文件进行合并
-M:将前面3个字母依照月份的缩写进行排序
-n:依照数值的大小排序
-o <输出文件>:将排序后的结果存入原文件
-r:以相反的顺序来排序,
-k:指定需要排序的栏位
-t <分隔字符>:指定排序时所用的栏位分隔字符
-u:去除重复的行
4、命令示例
4.1 sort将文件/文本的每一行作为一个单位,相互比较,比较原则是从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出:
1 [root@lzg ~]# cat a.txt 2 5 3 11 4 1 5 156 6 11 7 23 8 156 9 256 10 [root@lzg ~]# sort a.txt 11 1 12 11 13 11 14 156 15 156 16 23 17 256 18 5 19 [root@lzg ~]# cat b.txt 20 aaa:30:1.6 21 bbb:10:2.5 22 ccc:50:3.3 23 ddd:20:4.2 24 bbb:10:2.5 25 eee:80:5.4 26 eee:60:5.1 27 [root@lzg ~]# sort b.txt 28 aaa:30:1.6 29 bbb:10:2.5 30 bbb:10:2.5 31 ccc:50:3.3 32 ddd:20:4.2 33 eee:60:5.1 34 eee:80:5.4 35 [root@lzg ~]#
4.2 忽略相同行使用-u选项或者uniq:
1 [root@lzg ~]# sort a.txt 2 1 3 11 4 11 5 156 6 156 7 23 8 256 9 5 10 [root@lzg ~]# sort -u a.txt 11 1 12 11 13 156 14 23 15 256 16 5 17 [root@lzg ~]# sort b.txt 18 aaa:30:1.6 19 bbb:10:2.5 20 bbb:10:2.5 21 ccc:50:3.3 22 ddd:20:4.2 23 eee:60:5.1 24 eee:80:5.4 25 [root@lzg ~]# sort -u b.txt 26 aaa:30:1.6 27 bbb:10:2.5 28 ccc:50:3.3 29 ddd:20:4.2 30 eee:60:5.1 31 eee:80:5.4 32 [root@lzg ~]#
或者使用uniq:
1 [root@lzg ~]# sort a.txt 2 1 3 11 4 11 5 156 6 156 7 23 8 256 9 5 10 [root@lzg ~]# sort a.txt | uniq 11 1 12 11 13 156 14 23 15 256 16 5 17 [root@lzg ~]# sort b.txt 18 aaa:30:1.6 19 bbb:10:2.5 20 bbb:10:2.5 21 ccc:50:3.3 22 ddd:20:4.2 23 eee:60:5.1 24 eee:80:5.4 25 [root@lzg ~]# sort b.txt | uniq 26 aaa:30:1.6 27 bbb:10:2.5 28 ccc:50:3.3 29 ddd:20:4.2 30 eee:60:5.1 31 eee:80:5.4 32 [root@lzg ~]#
4.3 -n 依照数值的大小顺序排列:
1 [root@lzg ~]# cat a.txt 2 5 3 11 4 1 5 156 6 11 7 23 8 156 9 256 10 [root@lzg ~]# sort -n a.txt 11 1 12 5 13 11 14 11 15 23 16 156 17 156 18 256 19 [root@lzg ~]# cat b.txt 20 aaa:30:1.6 21 bbb:10:2.5 22 ccc:50:3.3 23 ddd:20:4.2 24 bbb:10:2.5 25 eee:80:5.4 26 eee:60:5.1 27 [root@lzg ~]# sort -n b.txt 28 aaa:30:1.6 29 bbb:10:2.5 30 bbb:10:2.5 31 ccc:50:3.3 32 ddd:20:4.2 33 eee:60:5.1 34 eee:80:5.4 35 [root@lzg ~]#
4.4 -o <输出文件>:将排序后的结果存入原文件。
由于sort默认是把结果输出到标准输出,所以需要用重定向才能将结果写入文件,形如sort filename > newfile。但是,如果你想把排序结果输出到原文件中,用重定向可就不行了。
1 [root@lzg ~]# ls 2 a.txt b.txt c.txt 3 [root@lzg ~]# cat c.txt 4 8 5 1 6 3 7 2 8 [root@lzg ~]# sort c.txt 9 1 10 2 11 3 12 8 13 [root@lzg ~]# sort c.txt > d.txt #输出结果重定向到d.txt 14 [root@lzg ~]# ls 15 a.txt b.txt c.txt d.txt 16 [root@lzg ~]# cat d.txt #查看内容,排序后的 17 1 18 2 19 3 20 8 21 [root@lzg ~]# sort c.txt > c.txt #类似方法重定向到原文件 c.txt 22 [root@lzg ~]# ls 23 a.txt b.txt c.txt d.txt 24 [root@lzg ~]# cat c.txt #查看内容,将原文件清空了 25 [root@lzg ~]#
这个时候可以使用 -o 选项,将结果写入原文件。
1 [root@lzg ~]# cat c.txt 2 8 3 1 4 3 5 2 6 [root@lzg ~]# sort c.txt -o c.txt 7 [root@lzg ~]# cat c.txt 8 1 9 2 10 3 11 8 12 [root@lzg ~]#
4.5 -c:检查文件是否已经按照顺序排序
1 [root@lzg ~]# cat a.txt 2 5 dsfsd 5000 300 3 1 hyj 1000 600 4 156 efcv 4000 700 5 11 ghjty 600 566 6 23 rfdfg 7569 500 7 256 hjtf 6000 230 8 [root@lzg ~]# sort -c a.txt 9 sort: a.txt:2: disorder: 1 hyj 1000 600 #提示无序 10 [root@lzg ~]# cat c.txt 11 1 12 2 13 3 14 8 15 [root@lzg ~]# sort -c c.txt #已排序的,没有提示 16 [root@lzg ~]#
4.6 sort的-n、-r、-k、-t选项的使用:
1 [root@lzg ~]# cat b.txt 2 aaa:30:1.6 3 bbb:10:2.5 4 ccc:50:3.3 5 ddd:20:4.2 6 bbb:10:2.5 7 eee:80:5.4 8 eee:60:5.1 9 [root@lzg ~]# sort -n -k2 -t: b.txt 10 bbb:10:2.5 11 bbb:10:2.5 12 ddd:20:4.2 13 aaa:30:1.6 14 ccc:50:3.3 15 eee:60:5.1 16 eee:80:5.4 17 [root@lzg ~]# sort -nr -k3 -t: b.txt 18 eee:80:5.4 19 eee:60:5.1 20 ddd:20:4.2 21 ccc:50:3.3 22 bbb:10:2.5 23 bbb:10:2.5 24 aaa:30:1.6 25 [root@lzg ~]#
#-n是按照数字大小排序,-r是以相反顺序,-k是指定需要排序的栏位、2是代表第二栏的,-t指定栏位分隔符为冒号
有时候学习脚本,你会发现sort命令后面跟了一堆类似-k1,2,或者-k1.2 -k3.4的东东,有些匪夷所思。今天,我们就来搞定它—-k选项!
1、准备以下素材,test.txt 第一个域是公司名称,第二个域是公司人数,第三个域是员工平均工资,a.txt 和 b.txt :
1 [root@lzg ~]# cat test.txt 2 zaidu 200 5000 3 guge 50 3000 4 sohu 80 4500 5 google 210 5000 6 zdfg 150 900 7 hyke 80 800 8 [root@lzg ~]# cat a.txt 9 5 dsfsd 5000 300 10 1 hyj 1000 600 11 156 efcv 4000 700 12 11 ghjty 800 500 13 23 rfdfg 600 500 14 256 hjtf 4000 230 15 [root@lzg ~]# cat b.txt 16 dc:30:1.6:df 17 ab:10:2.5:1c 18 4f:50:3.3:d6 19 dd:20:4.2:8a 20 bb:10:2.5:f1 21 5e:80:5.4:e3 22 6e:60:5.1:ad 23 [root@lzg ~]#
2、让 test.txt 按公司的字母顺序排序,也就是按第一个域进行排序:(这个test.txt文件有三个域)
1 [root@lzg ~]# sort test.txt #默认按照升序自动排序 2 google 210 5000 3 guge 50 3000 4 hyke 80 800 5 sohu 80 4500 6 zaidu 200 5000 7 zdfg 150 900 8 或者 9 [root@lzg ~]# sort -t' ' -k1 test.txt #-t' '指定栏位分隔符为空格,-k1指定需要排序的栏位是1栏位 10 google 210 5000 11 guge 50 3000 12 hyke 80 800 13 sohu 80 4500 14 zaidu 200 5000 15 zdfg 150 900 16 或者 17 [root@lzg ~]# sort -t ' ' -k 1 test.txt #-t和-k的可以用空格分开 18 google 210 5000 19 guge 50 3000 20 hyke 80 800 21 sohu 80 4500 22 zaidu 200 5000 23 zdfg 150 900 24 [root@lzg ~]#
以上开头是以字母开始的,如果是数字呢?
1 [root@lzg ~]# sort a.txt 2 11 ghjty 800 500 3 156 efcv 4000 700 4 1 hyj 1000 600 5 23 rfdfg 600 500 6 256 hjtf 4000 230 7 5 dsfsd 5000 300 8 [root@lzg ~]# sort -t' ' -k1 a.txt #此处不加-t' '也可以,因为排序的是第一个域,如果是后面的,需要加 9 11 ghjty 800 500 10 156 efcv 4000 700 11 1 hyj 1000 600 12 23 rfdfg 600 500 13 256 hjtf 4000 230 14 5 dsfsd 5000 300 15 [root@lzg ~]#
以上可以看到数字排序不是按照正常数值大小的方式排序的,而是按照首字符的大小排序,那这种情况怎么办呢?请看下面的方式
1 [root@lzg ~]# sort -n a.txt 2 1 hyj 1000 600 3 5 dsfsd 5000 300 4 11 ghjty 800 500 5 23 rfdfg 600 500 6 156 efcv 4000 700 7 256 hjtf 4000 230 8 [root@lzg ~]# sort -k1n a.txt #标准写法 9 1 hyj 1000 600 10 5 dsfsd 5000 300 11 11 ghjty 800 500 12 23 rfdfg 600 500 13 156 efcv 4000 700 14 256 hjtf 4000 230 15 [root@lzg ~]#
1 [root@lzg ~]# sort -k3n a.txt 2 23 rfdfg 600 500 3 11 ghjty 800 500 4 1 hyj 1000 600 5 156 efcv 4000 700 6 256 hjtf 4000 230 7 5 dsfsd 5000 300 8 [root@lzg ~]# sort -t' ' -k3n a.txt 9 11 ghjty 800 500 10 156 efcv 4000 700 11 1 hyj 1000 600 12 23 rfdfg 600 500 13 5 dsfsd 5000 300 14 256 hjtf 4000 230 15 [root@lzg ~]#
以上可以看到用空格隔开的不加 -t' ' 也可以。注意:空格的需要加单引号 '' ,其他类型的可不加,但最好是加单引号,看下面的例子:
1 [root@lzg ~]# sort -t: -k2n b.txt 2 ab:10:2.5:1c 3 bb:10:2.5:f1 4 dd:20:4.2:8a 5 dc:30:1.6:df 6 4f:50:3.3:d6 7 6e:60:5.1:ad 8 5e:80:5.4:e3 9 [root@lzg ~]# sort -t':' -k2n b.txt 10 ab:10:2.5:1c 11 bb:10:2.5:f1 12 dd:20:4.2:8a 13 dc:30:1.6:df 14 4f:50:3.3:d6 15 6e:60:5.1:ad 16 5e:80:5.4:e3 17 [root@lzg ~]#
3、我想让 test.txt 按照公司人数排序:
1 [root@lzg ~]# sort -t' ' -k2n test.txt 2 guge 50 3000 3 hyke 80 800 4 sohu 80 4500 5 zdfg 150 900 6 zaidu 200 5000 7 google 210 5000 8 [root@lzg ~]#
此处出现一个问题,那就是hyke和sohu的公司人数相同,这个时候怎么办呢?按照默认规矩,是从第一个域开始进行升序排序,因此hyke排在了sohu前面。
4、我想让 test.txt 按照公司人数排序 ,人数相同的按照员工平均工资升序排序:
1 [root@lzg ~]# sort -t' ' -k2n -k3n test.txt 2 guge 50 3000 3 hyke 80 800 4 sohu 80 4500 5 zdfg 150 900 6 zaidu 200 5000 7 google 210 5000 8 [root@lzg ~]#
加了一个-k2 -k3就解决了问题。sort支持这种设定,就是说设定域排序的优先级,先以第2个域进行排序,如果相同,再以第3个域进行排序。(如果你愿意,可以一直这么写下去,设定很多个排序优先级)
5、我想让 test.txt 按照员工工资降序排序,如果员工工资相同的,则按照公司人数升序排序:
1 [root@lzg ~]# sort -t' ' -k3nr -k2n test.txt #n必须要跟在-k的后面 2 zaidu 200 5000 3 google 210 5000 4 sohu 80 4500 5 guge 50 3000 6 zdfg 150 900 7 hyke 80 800 8 [root@lzg ~]#
6、-k选项的具体语法格式:
-k选项的语法格式如下:
[ FStart [ .CStart ] ] [ Modifier ] [ , [ FEnd [ .CEnd ] ][ Modifier ] ]
这个语法格式可以被其中的逗号(“,”)分为两大部分,Start部分和End部分。
先给你灌输一个思想,那就是“如果不设定End部分,那么就认为End被设定为行尾”。这个概念很重要的,但往往你不会重视它。
Start部分也由三部分组成,其中的Modifier部分就是我们之前说过的类似n和r的选项部分。我们重点说说Start部分的FStart和C.Start。
C.Start也是可以省略的,省略的话就表示从本域的开头部分开始。之前例子中的-k 2和-k 3就是省略了C.Start的例子喽。
FStart.CStart,其中FStart就是表示使用的域,而CStart则表示在FStart域中从第几个字符开始算“排序首字符”。
同理,在End部分中,你可以设定FEnd.CEnd,如果你省略.CEnd,则表示结尾到“域尾”,即本域的最后一个字符。或者,如果你将CEnd设定为0(零),也是表示结尾到“域尾”。
7、test.txt 从公司英文名称的第二个字母开始进行排序:
1 [root@lzg ~]# sort -t' ' -k1.2 test.txt 2 zaidu 200 5000 3 zdfg 150 900 4 sohu 80 4500 5 google 210 5000 6 guge 50 3000 7 hyke 80 800 8 [root@lzg ~]#
使用 -k 1.2,这就表示从第一个域的第二个字符开始到本域的最后一个字符为止的字符串进行排序。你会发现zaid因为第二个字母是a而名列榜首。sohu和 google第二个字符都是o,但sohu的h在google的o前面,所以sohu排在googl的前面。
8、只针对公司英文名称的第二个字母进行排序,如果相同的按照员工工资进行降序排序:
1 [root@lzg ~]# sort -t' ' -k1.2,1.2 -k3,3nr test.txt 2 zaidu 200 5000 3 zdfg 150 900 4 google 210 5000 5 sohu 80 4500 6 guge 50 3000 7 hyke 80 800 8 [root@lzg ~]#
由于只对第二个字母进行排序,所以我们使用了-k 1.2,1.2的表示方式,表示我们“只”对第二个字母进行排序。(如果你问“我使用-k 1.2怎么不行?”,当然不行,因为你省略了End部分,这就意味着你将对从第二个字母起到本域最后一个字符为止的字符串进行排序)。对于员工工资进行排序,我们也使用了-k 3,3,这是最准确的表述,表示我们“只”对本域进行排序,因为如果你省略了后面的3,就变成了我们“对第3个域开始到最后一个域位置的内容进行排序” 了。
9、在modifier部分还可以用到哪些选项?
可以用到b、d、f、i、n 或 r。
其中n和r你肯定已经很熟悉了。
b表示忽略本域的签到空白符号。
d表示对本域按照字典顺序排序(即,只考虑空白和字母)。
f表示对本域忽略大小写进行排序。
10、关于-k和-u联合使用的例子:
1 [root@lzg ~]# cat face.txt 2 google 110 5000 3 baidu 100 5000 4 guge 50 3000 5 sohu 100 4500 6 [root@lzg ~]# sort -t' ' -k2n face.txt 7 guge 50 3000 8 baidu 100 5000 9 sohu 100 4500 10 google 110 5000 11 [root@lzg ~]# sort -t' ' -k2n -u face.txt 12 guge 50 3000 13 baidu 100 5000 14 google 110 5000 15 [root@lzg ~]#
当设定以公司员工域进行数值排序,然后加-u后,sohu一行就被删除了!原来-u只识别用-k设定的域,发现相同,就将后续相同的行都删除。
1 [root@lzg ~]# sort -k1.1,1.1 -u face.txt 2 baidu 100 5000 3 google 110 5000 4 sohu 100 4500 5 [root@lzg ~]#
这个例子也同理,开头字符是g的guge就没有幸免于难。
1 [root@lzg ~]# sort -n -k2 -k3 -u face.txt 2 guge 50 3000 3 sohu 100 4500 4 baidu 100 5000 5 google 110 5000 6 [root@lzg ~]#
这里设置了两层排序优先级的情况下,使用-u就没有删除任何行。原来-u是会权衡所有-k选项,将都相同的才会删除,只要其中有一级不同都不会轻易删除的
11、另类排序:
1 [root@lzg ~]# sort -n -k2.2,3.1 face.txt 2 guge 50 3000 3 baidu 100 5000 4 sohu 100 4500 5 google 110 5000 6 [root@lzg ~]#
以第二个域的第二个字符开始到第三个域的第一个字符结束的部分进行排序。
第一行,会提取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前面了,用一个例子即可证明,如下:
1 [root@lzg ~]# sort -n -k2.2,3.1 -k1.1r face.txt 2 guge 50 3000 3 sohu 100 4500 4 baidu 100 5000 5 google 110 5000 6 [root@lzg ~]#
其实上面的例子类似下面的例子:
1 [root@lzg ~]# sort -n -k2.2,3.1 face.txt 2 guge 50 3000 3 baidu 100 5000 4 sohu 100 4500 5 google 110 5000 6 [root@lzg ~]# sort -n -k2.2 face.txt 7 guge 50 3000 8 baidu 100 5000 9 sohu 100 4500 10 google 110 5000 11 [root@lzg ~]#
12、有时候在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个字符。)