Linux sort排序命令介绍
简介
Linux sort 命令用于将文本文件内容加以排序。
sort 可针对文本文件的内容,以行为单位来排序。
语法
1 | sort [-bcdfimMnr][-o<输出文件>][-t<分隔字符>][+<起始栏位>-<结束栏位>][--help][--verison][文件][-k field1[,field2]] |
参数说明:
- -b 忽略每行前面开始出的空格字符。
- -c 检查文件是否已经按照顺序排序。
- -d 排序时,处理英文字母、数字及空格字符外,忽略其他的字符。
- -f 排序时,将小写字母视为大写字母。
- -i 排序时,除了040至176之间的ASCII字符外,忽略其他的字符。
- -m 将几个排序好的文件进行合并。
- -M 将前面3个字母依照月份的缩写进行排序。
- -n 依照数值的大小排序。
- -u 意味着是唯一的(unique),输出的结果是去完重了的。
- -o<输出文件> 将排序后的结果存入指定的文件。
- -r 以相反的顺序来排序。
- -t<分隔字符> 指定排序时所用的栏位分隔字符。
- +<起始栏位>-<结束栏位> 以指定的栏位来排序,范围由起始栏位到结束栏位的前一栏位。
- --help 显示帮助。
- --version 显示版本信息。
- [-k field1[,field2]] 按指定的列进行排序。
1 sort的工作原理
sort将文件的每一行作为一个单位,相互比较,比较原则是从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出。
1 2 3 4 5 6 7 8 9 10 | [root@xmmup ~]$ cat seq.txt banana apple pear orange [root@xmmup ~]$ sort seq.txt apple banana orange pear |
2 sort的-u选项
它的作用很简单,就是在输出行中去除重复行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [root@xmmup ~]$ cat seq.txt banana apple pear orange pear [root@xmmup ~]$ sort seq.txt apple banana orange pear pear [root@xmmup ~]$ sort -u seq.txt apple banana orange pear |
pear由于重复被-u选项无情的删除了。
3 sort的-r选项
sort默认的排序方式是升序,如果想改成降序,就加个-r就搞定了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | [root@xmmup ~]$ cat number.txt 1 3 5 2 4 [root@xmmup ~]$ sort number.txt 1 2 3 4 5 [root@xmmup ~]$ sort -r number.txt 5 4 3 2 1 |
4 sort的-o选项
由于sort默认是把结果输出到标准输出,所以需要用重定向才能将结果写入文件,形如sort filename > newfile。
但是,如果你想把排序结果输出到原文件中,用重定向可就不行了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | [root@xmmup ~]$ sort -r number.txt > number.txt [root@xmmup ~]$ cat number.txt [root@xmmup ~]$ 看,竟然将number清空了。 就在这个时候,-o选项出现了,它成功的解决了这个问题,让你放心的将结果写入原文件。这或许也是-o比重定向的唯一优势所在。 [root@xmmup ~]$ cat number.txt 1 3 5 2 4 [root@xmmup ~]$ sort -r number.txt -o number.txt [root@xmmup ~]$ cat number.txt 5 4 3 2 1 |
5 sort的-n选项
你有没有遇到过10比2小的情况。我反正遇到过。出现这种情况是由于排序程序将这些数字按字符来排序了,排序程序会先比较1和2,显然1小,所以就将10放在2前面喽。这也是sort的一贯作风。
我们如果想改变这种现状,就要使用-n选项,来告诉sort,“要以数值来排序”!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | [root@xmmup ~]$ cat number.txt 1 10 19 11 2 5 [root@xmmup ~]$ sort number.txt 1 10 11 19 2 5 [root@xmmup ~]$ sort -n number.txt 1 2 5 10 11 19 |
6 sort的-t选项和-k选项
如果有一个文件的内容是这样:
1 2 3 4 5 | [root@xmmup ~]$ cat 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来指定列数了。
1 2 3 4 5 | [root@xmmup ~]$ sort -n -k 2 -t : facebook.txt apple:10:2.5 orange:20:3.4 banana:30:5.5 pear:90:2.3 |
我们使用冒号作为间隔符,并针对第二列来进行数值升序排序,结果很令人满意。
7 其他的sort常用选项
-f会将小写字母都转换为大写字母来进行比较,亦即忽略大小写
-c会检查文件是否已排好序,如果乱序,则输出第一个乱序的行的相关信息,最后返回1
-C会检查文件是否已排好序,如果乱序,不输出内容,仅返回1
-M会以月份来排序,比如JAN小于FEB等等
-b会忽略每一行前面的所有空白部分,从第一个可见字符开始比较。
其它
有时候学习脚本,你会发现sort命令后面跟了一堆类似-k1,2,或者-k1.2 -k3.4的东东,有些匪夷所思。今天,我们就来搞定它—-k选项!
1 准备素材
$ cat facebook.txt
google 110 5000
baidu 100 5000
guge 50 3000
sohu 100 4500
第一个域是公司名称,第二个域是公司人数,第三个域是员工平均工资。
2 我想让这个文件按公司的字母顺序排序,也就是按第一个域进行排序:(这个facebook.txt文件有三个域)
1 2 3 4 5 | $ sort -t ' ' -k 1 facebook.txt baidu 100 5000 google 110 5000 guge 50 3000 sohu 100 4500 |
看到了吧,就直接用-k 1设定就可以了。(其实此处并不严格,稍后你就会知道)
3 我想让facebook.txt按照公司人数排序
1 2 3 4 5 | $ sort -n -t ' ' -k 2 facebook.txt guge 50 3000 baidu 100 5000 sohu 100 4500 google 110 5000 |
不用解释,我相信你能懂。
但是,此处出现了问题,那就是baidu和sohu的公司人数相同,都是100人,这个时候怎么办呢?按照默认规矩,是从第一个域开始进行升序排序,因此baidu排在了sohu前面。
4 我想让facebook.txt按照公司人数排序 ,人数相同的按照员工平均工资升序排序:
1 2 3 4 5 | $ sort -n -t ' ' -k 2 -k 3 facebook.txt guge 50 3000 sohu 100 4500 baidu 100 5000 google 110 5000 |
看,我们加了一个-k2 -k3就解决了问题。对滴,sort支持这种设定,就是说设定域排序的优先级,先以第2个域进行排序,如果相同,再以第3个域进行排序。(如果你愿意,可以一直这么写下去,设定很多个排序优先级)
5 我想让facebook.txt按照员工工资降序排序,如果员工人数相同的,则按照公司人数升序排序:(这个有点难度喽)
1 2 3 4 5 | $ sort -n -t ' ' -k 3r -k 2 facebook.txt baidu 100 5000 google 110 5000 sohu 100 4500 guge 50 3000 |
此处有使用了一些小技巧,你仔细看看,在-k 3后面偷偷加上了一个小写字母r。你想想,再结合我们上一篇文章,能得到答案么?揭晓:r和-r选项的作用是一样的,就是表示逆序。因为sort默认是按照升序排序的,所以此处需要加上r表示第三个域(员工平均工资)是按照降序排序。此处你还可以加上n,就表示对这个域进行排序时,要按照数值大小进行排序,举个例子吧:
1 2 3 4 5 | $ sort -t ' ' -k 3nr -k 2n facebook.txt baidu 100 5000 google 110 5000 sohu 100 4500 guge 50 3000 |
看,我们去掉了最前面的-n选项,而是将它加入到了每一个-k选项中了。
6 -k选项的具体语法格式
要继续往下深入的话,就不得不来点理论知识。你需要了解-k选项的语法格式,如下:
1 | [ 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 突发奇想,从公司英文名称的第二个字母开始进行排序:
1 2 3 4 5 | $ sort -t ' ' -k 1.2 facebook.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只能屈居第四了。
8 又突发奇想,,只针对公司英文名称的第二个字母进行排序,如果相同的按照员工工资进行降序排序:
1 2 3 4 5 | $ sort -t ' ' -k 1.2,1.2 -k 3,3nr facebook.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个域开始到最后一个域位置的内容进行排序” 了。
9 在modifier部分还可以用到哪些选项?
可以用到b、d、f、i、n 或 r。
其中n和r你肯定已经很熟悉了。
b表示忽略本域的签到空白符号。
d表示对本域按照字典顺序排序(即,只考虑空白和字母)。
f表示对本域忽略大小写进行排序。
i表示忽略“不可打印字符”,只针对可打印字符进行排序。(有些ASCII就是不可打印字符,比如\a是报警,\b是退格,\n是换行,\r是回车等等)
10 思考思考关于-k和-u联合使用的例子:
1 2 3 4 5 | $ cat facebook.txt google 110 5000 baidu 100 5000 guge 50 3000 sohu 100 4500 |
这是最原始的facebook.txt文件。
1 2 3 4 5 6 7 8 9 10 | $ sort -n -k 2 facebook.txt guge 50 3000 baidu 100 5000 sohu 100 4500 google 110 5000 $ sort -n -k 2 -u facebook.txt guge 50 3000 baidu 100 5000 google 110 5000 |
当设定以公司员工域进行数值排序,然后加-u后,sohu一行就被删除了!原来-u只识别用-k设定的域,发现相同,就将后续相同的行都删除。
1 2 3 4 5 6 7 8 9 10 | $ sort -k 1 -u facebook.txt baidu 100 5000 google 110 5000 guge 50 3000 sohu 100 4500 $ sort -k 1.1,1.1 -u facebook.txt baidu 100 5000 google 110 5000 sohu 100 4500 |
这个例子也同理,开头字符是g的guge就没有幸免于难。
1 2 3 4 5 | $ sort -n -k 2 -k 3 -u facebook.txt guge 50 3000 sohu 100 4500 baidu 100 5000 google 110 5000 |
咦!这里设置了两层排序优先级的情况下,使用-u就没有删除任何行。原来-u是会权衡所有-k选项,将都相同的才会删除,只要其中有一级不同都不会轻易删除的:)(不信,你可以自己加一行sina 100 4500试试看)
11 最诡异的排序:
1 2 3 4 5 | $ sort -n -k 2.2,3.1 facebook.txt guge 50 3000 baidu 100 5000 sohu 100 4500 google 110 5000 |
以第二个域的第二个字符开始到第三个域的第一个字符结束的部分进行排序。
第一行,会提取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 2 3 4 5 | $ sort -n -k 2.2,3.1 -k 1,1r facebook.txt guge 50 3000 sohu 100 4500 baidu 100 5000 google 110 5000 |
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个字符。明白?)
参考:https://www.cnblogs.com/51linux/archive/2012/05/23/2515299.html
综合示例
若想找出“/var/log/secure*”文件中密码输入错误的IP地址,并且分组排序,找出尝试次数最多的前10个IP地址:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | grep -E 'Failed password' /var/log/secure* | awk -F" " '{print $11; print $13}' | sort | uniq -c [root@xmmup log]# ll /var/log/secure* -rw------- 1 root root 6690164 Sep 30 10:54 /var/log/secure -rw------- 1 root root 13584968 Sep 5 02:38 /var/log/secure-20210905 -rw------- 1 root root 668720 Sep 12 02:58 /var/log/secure-20210912 -rw------- 1 root root 4867269 Sep 19 00:48 /var/log/secure-20210919 -rw------- 1 root root 8802975 Sep 26 03:07 /var/log/secure-20210926 [root@xmmup log]# grep -E 'Failed password' /var/log/secure* | more /var/log/secure:Sep 26 03:08:19 xmmup sshd[18746]: Failed password for root from 159.65.198.14 port 56144 ssh2 /var/log/secure:Sep 26 03:08:57 xmmup sshd[18825]: Failed password for root from 159.65.198.14 port 60928 ssh2 /var/log/secure:Sep 26 03:09:37 xmmup sshd[18900]: Failed password for root from 159.65.198.14 port 37060 ssh2 /var/log/secure:Sep 26 03:10:20 xmmup sshd[18989]: Failed password for root from 159.65.198.14 port 41652 ssh2 /var/log/secure:Sep 26 03:11:00 xmmup sshd[19061]: Failed password for root from 159.65.198.14 port 46286 ssh2 /var/log/secure:Sep 26 03:11:42 xmmup sshd[19137]: Failed password for root from 159.65.198.14 port 50798 ssh2 /var/log/secure:Sep 26 03:12:23 xmmup sshd[19212]: Failed password for root from 159.65.198.14 port 55344 ssh2 /var/log/secure:Sep 26 03:13:03 xmmup sshd[19284]: Failed password for root from 159.65.198.14 port 59970 ssh2 /var/log/secure:Sep 26 03:13:46 xmmup sshd[19373]: Failed password for root from 159.65.198.14 port 36450 ssh2 /var/log/secure:Sep 26 03:14:29 xmmup sshd[19448]: Failed password for root from 159.65.198.14 port 40900 ssh2 /var/log/secure:Sep 26 03:15:08 xmmup sshd[19523]: Failed password for root from 159.65.198.14 port 45452 ssh2 /var/log/secure:Sep 26 03:15:43 xmmup sshd[19589]: Failed password for invalid user mi from 139.196.252.217 port 44831 ssh2 /var/log/secure:Sep 26 03:15:48 xmmup sshd[19598]: Failed password for root from 159.65.198.14 port 50104 ssh2 /var/log/secure:Sep 26 03:16:30 xmmup sshd[19674]: Failed password for root from 159.65.198.14 port 54628 ssh2 /var/log/secure:Sep 26 03:16:46 xmmup sshd[19705]: Failed password for invalid user samir from 139.196.252.217 port 46198 ssh2 /var/log/secure:Sep 26 03:16:54 xmmup sshd[19722]: Failed password for root from 139.196.252.217 port 46393 ssh2 /var/log/secure:Sep 26 03:17:02 xmmup sshd[19739]: Failed password for invalid user elliott from 139.196.252.217 port 46587 ssh2 /var/log/secure:Sep 26 03:17:13 xmmup sshd[19758]: Failed password for root from 159.65.198.14 port 59188 ssh2 /var/log/secure:Sep 26 03:17:19 xmmup sshd[19772]: Failed password for invalid user larry from 139.196.252.217 port 46978 ssh2 /var/log/secure:Sep 26 03:17:36 xmmup sshd[19804]: Failed password for invalid user ernie from 139.196.252.217 port 47368 ssh2 /var/log/secure:Sep 26 03:17:57 xmmup sshd[19842]: Failed password for root from 159.65.198.14 port 35568 ssh2 /var/log/secure:Sep 26 03:18:10 xmmup sshd[19867]: Failed password for invalid user test from 139.196.252.217 port 48148 ssh2 /var/log/secure:Sep 26 03:18:39 xmmup sshd[19917]: Failed password for root from 159.65.198.14 port 40122 ssh2 /var/log/secure:Sep 26 03:19:08 xmmup sshd[19972]: Failed password for invalid user cd from 139.196.252.217 port 49513 ssh2 --More-- ..... [root@xmmup log]# grep -E 'Failed password' /var/log/secure* | awk -F" " '{print $11; print $13}' | sort | uniq -c | sort -n -r | grep -E "([0-9]{1,3}.){3}[0-9]{1,3}" | awk -F" " '{ if($1>20){print $0}} ' | head -n 20 18210 47.96.159.147 10098 115.29.203.30 8159 39.108.1.31 4488 220.174.25.172 4488 219.128.250.146 3205 106.14.58.100 3112 115.231.83.83 1969 159.65.198.14 1486 164.90.185.71 1467 64.225.51.255 1110 116.62.118.5 790 167.172.44.36 665 164.90.182.90 361 136.144.41.95 306 167.99.248.235 279 165.232.155.116 279 137.184.83.4 261 122.234.56.186 245 122.234.63.133 217 207.154.255.34 |
这样就可以找到哪些IP地址多次尝试以错误的用户名或密码暴力破解服务器了,相关处理办法参考:https://www.xmmup.com/linuxfuwuqianquanjiagu.html