sort和awk是Linux平台上两个超强的排序、过滤命令。完整叙述它们的命令用法、以及组合用法是不可能的。这里,只记录我遇到问题,和对应的解决办法,权当参考。
1 sort命令
先说sort命令,这个命令用于将文本文件内容加以排序。
sort将文件的每一行作为一个单位,相互比较,比较原则是从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出。为了简单说明,列举一个文件worker.txt,5列信息分别是:姓的拼音、称呼、工号、年龄、月薪。
$ cat worker.txt
zhang 小张 5 25 8000
li 小李 3 29 7500
wang 小王 7 21 6800
zhao 小照 2 33 12000
ma 小马 10 30 6000
chen 老陈 6 46 32000
(1)如果不带任何参数,执行“sort worker.txt”,可以看到sort默认按第一列排序(其实背后原理不完全是这样的,但你可以这样理解)。
$ sort worker.txt
chen 老陈 6 46 32000
li 小李 3 29 7500
ma 小马 10 30 6000
wang 小王 7 21 6800
zhang 小张 5 25 8000
zhao 小照 2 33 12000
(2)想按N列排序,就是“sort -k N”,也可以把N与k挤一起“sort -kN”。
如果想按年龄(第4列)排序,执行:“sort -k 4 worker.txt”。
$ $ sort -k 4 worker.txt
wang 小王 7 21 6800
zhang 小张 5 25 8000
li 小李 3 29 7500
ma 小马 10 30 6000
zhao 小照 2 33 12000
chen 老陈 6 46 32000
如果想按第三列工号排序,则执行"sort -k3 worker.txt"
$ sort -k3 worker.txt
ma 小马 10 30 6000
zhao 小照 2 33 12000
li 小李 3 29 7500
zhang 小张 5 25 8000
chen 老陈 6 46 32000
wang 小王 7 21 6800
(3)要以数值来排序,那就要用“-n”选项。
上面的结果有点不对啊:怎么小马的工号10比小照的工号2还小呢?原来,出现这种情况,是由于排序程序将这些数字按字符来排序了,排序程序会先比较1和2,显然1小,所以就将10放在2前面。sort默认就是按字符排序,而不是数字来排序的。
改变默认的字符排序,要以数值来排序,那就要用“-n”选项。
$ sort -n -k 3 worker.txt
zhao 小照 2 33 12000
li 小李 3 29 7500
zhang 小张 5 25 8000
chen 老陈 6 46 32000
wang 小王 7 21 6800
ma 小马 10 30 6000
(4)"-r"反序排列
你看上面的结果,按数值排序“-n”,小马的工号为10,排到最后,这就正常了。但如果想反过来排序,数值由小到大。也对sort也是小儿科的操作,加“-r”参数就可以反序排列。
$ sort -n -r -k 3 worker.txt
ma 小马 10 30 6000
wang 小王 7 21 6800
chen 老陈 6 46 32000
zhang 小张 5 25 8000
li 小李 3 29 7500
zhao 小照 2 33 12000
好啦,有兴趣看到这里的小伙伴,我猜你肯定想,排序的如果不是一列,是两列或多列呢?比如上面的例子,按年龄由小到大,工资由低到高,怎么排序?
好吧,再往worker.txt加上一行“liu 小刘 1 29 24500”。
想排序几个列,sort就可以加几个"-kN"(N是列序号,从1开始)。
$ sort -k4n -k5n worker.txt
wang 小王 7 21 6800
zhang 小张 5 25 8000
li 小李 3 29 7500
liu 小刘 1 29 24500
ma 小马 10 30 6000
zhao 小照 2 33 12000
chen 老陈 6 46 32000
再按年龄由小到大,工资由高到低,怎么排序?-别忘记反序参数“-r”了。
$ sort -k4n -k5nr worker.txt
wang 小王 7 21 6800
zhang 小张 5 25 8000
liu 小刘 1 29 24500
li 小李 3 29 7500
ma 小马 10 30 6000
zhao 小照 2 33 12000
chen 老陈 6 46 32000
上面就可以看到,同龄人小刘和小李,工资低的排下面了。
好吧,sort功能强大,参数复杂,我们一般明白它是按列排序,知道k、n、r这些参数就可以满足一般要求了。
2 awk命令
sort命令,已经够强够复杂的了。awk命令,更是复杂强大的没法说。不服,看看它的定义就知道了:AWK 是一种处理文本文件的语言,是一个强大的文本分析工具,之所以叫 AWK 是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。
你见过有几个命令,能被人称之为“语言”的?没有绝艺在身,怎敢大言不惭!awk号称Linux平台上的三剑客之一,果然不负盛名。(其它两个分别是grep和sed。)《The AWK Programming Language》一书,200多页,看着头昏脑涨,我这里只关心awk的print参数用法。
(1)awk print
awk有内置的变量。对于每一个记录,即行,分隔空白字符分隔记录默认情况下,它存储在$ n个变量。如果该行有4个词,它会被存储在$ 1,$2,$ 3和$N。 $0表示整行。 NF是一个内置变量,它代表这一行有多少个被分隔的列数。
比如说,我们只关心worker.txt中的年龄和工资,也就是第1、4、5列。"awk '{print $4,$5}' worker.txt"就可以只输出worker.txt中的第1、4、5列。
$ cat worker.txt
liu 小刘 1 29 24500
zhang 小张 5 25 8000
li 小李 3 29 7500
wang 小王 7 21 6800
zhao 小照 2 33 12000
ma 小马 10 30 6000
chen 老陈 6 46 32000
$ awk '{print $2,$4,$5}' worker.txt
小刘 29 24500
小张 25 8000
小李 29 7500
小王 21 6800
小照 33 12000
小马 30 6000
老陈 46 32000
(2)条件输出:awk if
worker.txt中,如果想输出年龄小于30的人的名字、年龄和工资,就需要用到awk中的if判断。年龄是第4列,也就是$4,判断语句就是“if ($4 < 30)”,命令如下:
$ awk '{if ($4 < 30) print $2,$4,$5}' worker.txt
小刘 29 24500
小张 25 8000
小李 29 7500
小王 21 6800
3 awk与sort组合
强强联手,只会更强。
我们只要worker.txt中的年龄和工资,也就是第2、4、5列,并且以工资排序,怎么办?
一种是先用awk过滤文件中的列,再根据新的列来sort排序。就像下面这样,先把worker.txt中的第2、4、5列输出,再用sort排序。
$ awk '{print $2,$4,$5}' worker.txt | sort -n -k3
小马 30 6000
小王 21 6800
小李 29 7500
小张 25 8000
小照 33 12000
小刘 29 24500
老陈 46 32000
$ awk '{if ($4 < 30) print $2,$4,$5}' worker.txt | sort -n -k3
小王 21 6800
小李 29 7500
小张 25 8000
小刘 29 24500
另一种办法,就是先sort排序原有文件中的列,再用awk过滤输出来。
$ sort -n -k5 worker.txt | awk '{print $2,$4,$5}'
小马 30 6000
小王 21 6800
小李 29 7500
小张 25 8000
小照 33 12000
小刘 29 24500
老陈 46 32000
4 实际运用
linux命令之多,浩如烟海,参数之杂,多如繁星。需求引导,用到再学。以使用为目的,适当延伸,是一个最实际最有效的办法。
某天,我突然想生成一个文件,记录/dev下的设备文件,要求如下:
- “ls -l /dev”是数据输出源,因为这个命令会列出/dev下面的设备文件,并带有主次ID信息。
- 要按照设备主ID由小到大、次ID号由小到大的顺序显示。
- 只想输出主ID、次ID和设备文件名
- fifo管道、socket、软/硬连接、目录,这些东西没有主/次设备号,所以要过滤掉。
- 把过滤结果输出到"dev_id.txt"文件中去
OK,带着这些要求,结合上面的记录心得,一步步地来实现。
(1)“ls -l /dev”输出源数据,这个没有啥可说的。
(2)要按照设备主ID由小到大、次ID号由小到大的顺序显示
这个要用到sort输出:sort -n -k5 -k6
$ ls -l /dev | sort -n -k5 -k6
总用量 0
drwxr-xr-x 2 root root 0 5月 30 10:41 hugepages
drwxr-xr-x 2 root root 0 5月 30 10:41 pts
crw-r----- 1 root kmem 1, 1 5月 30 10:41 mem
crw-rw-rw- 1 root root 1, 3 5月 30 10:41 null
crw-r----- 1 root kmem 1, 4 5月 30 10:41 port
crw-rw-rw- 1 root root 1, 5 5月 30 10:41 zero
crw-rw-rw- 1 root root 1, 7 5月 30 10:41 full
crw-rw-rw- 1 root root 1, 8 5月 30 10:41 random
crw-rw-rw- 1 root root 1, 9 5月 30 10:41 urandom
crw-r--r-- 1 root root 1, 11 5月 30 10:41 kmsg
crw--w---- 1 root tty 4, 0 5月 30 10:41 tty0
crw--w---- 1 root tty 4, 1 5月 30 10:41 tty1
crw--w---- 1 songguoya tty 4, 2 5月 30 10:41 tty2
crw------- 1 root tty 4, 3 5月 31 11:03 tty3
crw--w---- 1 root tty 4, 4 5月 31 11:04 tty4
crw--w---- 1 root tty 4, 5 5月 31 11:01 tty5
lrwxrwxrwx 1 root root 4 5月 30 10:41 rtc -> rtc0
... ...
(3)只想输出主ID、次ID和设备文件名
主ID、次ID和设备文件名这三列,对应的列序号是5、6和10。也就是使用awk的print参数:awk '{print $5,$6,$10}'
$ ls -l /dev | sort -n -k5 -k6 | awk '{print $5,$6,$10}'
0 5月
0 5月
1, 1 mem
1, 3 null
1, 4 port
1, 5 zero
1, 7 full
1, 8 random
1, 9 urandom
1, 11 kmsg
4, 0 tty0
4, 1 tty1
4, 2 tty2
4, 3 tty3
4, 4 tty4
4, 5 tty5
4 5月 ->
(4)过滤掉没有主次ID的行
经检查,发现有主次ID的行,列数为10。如果列数小于9,就是没有主次ID的项目。要使用awk的if参数:awk '{if (NF == 10) print $5,$6,$10}'
$ ls -l /dev | sort -n -k5 -k6 | awk '{if (NF == 10) print $5,$6,$10}'
1, 1 mem
1, 3 null
1, 4 port
1, 5 zero
1, 7 full
1, 8 random
1, 9 urandom
1, 11 kmsg
4, 0 tty0
...
4, 63 tty63
4, 64 ttyS0
...
4, 95 ttyS31
5, 0 tty
5, 1 console
5, 2 ptmx
5, 3 ttyprintk
7, 0 loop0
(5)把过滤结果输出到"dev_id.txt"文件中去
输出重定向>就可以了。
$ ls -l /dev | sort -n -k5 -k6 | awk '{if (NF == 10) print $5,$6,$10}' > dev_id.txt
其实,这个命令行并不完美,因为结果中还有一些逗号“,”混杂其中。可以借助tr命令把逗号转成空格,这样结果就很好了。
$ ls -l /dev | sort -n -k5 -k6 | awk '{if (NF == 10) print $5,$6,$10}' | tr ',' ' ' > dev_list.txt
最后,写完此文,感觉自己好像在记述上古时代的东西一般!
能看到此处的你,想必也是同道中人了,请留个痕迹。哈哈,大家上网是为了找乐子的,看到这里,如果你觉得无聊无用的话,请吐槽留言!