awk '!arr[$0]++'后跟文件,可以过滤掉重复的行。

如下面的文件经过处理。   

[root@centos7 ~]# cat fstab

#
# /etc/fstab
# /etc/fstab
# /etc/fstab
# /etc/fstab
# /etc/fstab
# Created by anaconda on Tue Jul 11 20:07:17 2017
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=2fe39e58-8f02-49bf-b4b3-a59c56dd3907 /                       xfs     defaults        0 0
UUID=2fe39e58-8f02-49bf-b4b3-a59c56dd3907 /                       xfs     defaults        0 0
UUID=2fe39e58-8f02-49bf-b4b3-a59c56dd3907 /                       xfs     defaults        0 0
UUID=2fe39e58-8f02-49bf-b4b3-a59c56dd3907 /                       xfs     defaults        0 0
UUID=2fe39e58-8f02-49bf-b4b3-a59c56dd3907 /                       xfs     defaults        0 0
UUID=a86604e8-9d34-4c3f-b3b3-fb3f3b72e356 /app                    xfs     defaults        0 0
UUID=e45a1265-9826-442b-9ce6-5f44440e8d0e /boot                   xfs     defaults        0 0
UUID=a86604e8-9d34-4c3f-b3b3-fb3f3b72e356 /app                    xfs     defaults        0 0
UUID=e45a1265-9826-442b-9ce6-5f44440e8d0e /boot                   xfs     defaults        0 0
UUID=f1eb3668-9539-4d4f-bae1-79f94029a101 swap                    swap    defaults        0 0
UUID=645fd6f5-da6f-458e-968d-02c7106a3b62 /home                   xfs 
defaults,usrquota,grpquota    0 2
[root@centos7 ~]# awk '!arr[$0]++' fstab

#
# /etc/fstab
# Created by anaconda on Tue Jul 11 20:07:17 2017
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
UUID=2fe39e58-8f02-49bf-b4b3-a59c56dd3907 /                       xfs     defaults        0 0
UUID=a86604e8-9d34-4c3f-b3b3-fb3f3b72e356 /app                    xfs     defaults        0 0
UUID=e45a1265-9826-442b-9ce6-5f44440e8d0e /boot                   xfs     defaults        0 0
UUID=f1eb3668-9539-4d4f-bae1-79f94029a101 swap                    swap    defaults        0 0
UUID=645fd6f5-da6f-458e-968d-02c7106a3b62 /home                   xfs     defaults,usrquota,grpquota    0 2

我们知道,awk是一门语言。它的语言格式和bash差别还是有些的。awk得益于高效的执行速率,使用time命令查看执行时间,awk里的循环远甩bash几条街。

awk '!arr[$0]++' a.txt会先执行!arr[$0],$0指整行,在此时把文件的整行当作关联数组的下标的意思。关联数组为下标为字符串的数组。awk和sed一样,都是对文件进行行处理。每次抽取一行,处理,再抽取下一行处理。

!是取反。

awk语言格式如下:

awk [options] 'BEGIN{ action;… } pattern{ action;… } END{action;… }' file ..

这里的PATTERN是,根据pattern条件,过滤匹配的行,再做处理

(1)如果未指定:空模式,匹配每一行 

(2) /regular expression/:仅处理能够模式匹配到的行,需要用/ /括起来 

    awk '/^UUID/{print $1}' /etc/fstab awk '!/^UUID/{print $1}' /etc/fstab 

(3) relational expression: 关系表达式,结果为“真”才会被处理 

    真:结果为非0值,非空字符串 

    假:结果为空字符串或0值

(4) line ranges:行范围 startline,endline:/pat1/,/pat2/ 不支持直接给出数字 格式 

    awk -F: ‘/^root\>/,/^nobody\>/{print $1}' /etc/passwd 

    awk -F: ‘(NR>=10&&NR<=20){print NR,$1}' /etc/passwd 

(5) BEGIN/END模式 BEGIN{}: 仅在开始处理文件中的文本之前执行一次 END{}:仅在文本处理完成之后执行一次

awk '!arr[$0]++' a.txt这里用到的是第三条。!arr$[0]++为这里的关系表达式,但因为++的值是在对arr$[0]值取反后进行,这里并不会影响输入结果。


我们知道i++是先对i赋值,再++,++i则是先增加1,再把+1的值赋予i。

[root@centos7 ~]# awk BEGIN'{i=1;print i++}'
1
[root@centos7 ~]# awk BEGIN'{i=1;print ++i}'
2
[root@centos7 ~]# awk BEGIN'{a=0;print a++}'
0
[root@centos7 ~]# awk BEGIN'{a=0;print ++a}'
1

而当i值为0时,print i++依旧输出0,结果为假。print ++i结果为1,即为真了。


awk对文本进行处理时,$0对应每行文本,即字符串,arr[$0]下面为字符串,是关联数组。

而当变量被赋值为字符串时,变量对应的数值则为0。

[root@centos7 ~]# awk BEGIN'{a="shhkjl";print a}'
shhkjl
[root@centos7 ~]# awk BEGIN'{a="shhkjl";print !a}'   
0
[root@centos7 ~]# awk BEGIN'{a="shhkjl";print a++}'
0
[root@centos7 ~]# awk BEGIN'{a="shhkjl";a++;print a}'  
1

这样,对其取反,值则为1。


awk如果不跟输出条件,就默认输出$0,即默认输出本行的全部内容。

arr[$0]第一次读取文件的第一行,因为文本内容被当作字符串处理,则arr[$0]一直为假,即arr[$0]=0,对arr[$0]取反则为真,即!arr[$0]=1,则会执行打印操作。

至于++,++的结果并不输出,所以对当前行关系表达式的真假判断不会造成影响,但++的结果会在对下一行进行处理时影响arr[$0]的结果,虽然下一行的arr[$0],又是另一个值了。


以上面fastb的处理为例。

awk首先抽取第一行,此时arr[$0]为arr[""],arr[""]的值为空字符串,即arr[""]=0,则有!arr[""]=1,打印本行。++是对arr[""]执行的操作。arr[""]原值为0,++后则为1。

也就是说!arr[$0]++,其实是两个分开的并行动作,!arr[$0]是对关系表达式取反,arr[$0]++对arr[$0]进行+1的操作。!arr[$0]决定本行是否打印,arr[$0]的值则因为下标值的不同每次更换,如果遇不到相同行,则根本不会再起作用。

arr数组的第一个数为arr[""],在处理完第一行后,arr[""]被赋值为1。

对第二行处理时arr[$0]就成了arr["#"],arr["#"]="#",字符串,则arr["#"]=0,!arr["#"]=1,打印。

只要变量值为字符串,则变量值均为0。0为假,其他数值则均为真。

[root@centos7 ~]# awk BEGIN'{a=-1;print !a}'        
0
[root@centos7 ~]# awk BEGIN'{a=10;print !a}'  
0

直到遇到相同的行,如上面fstab文件中有重复的# /etc/fstab行,在对第一个# /etc/fstab行执行完操作后,arr["# /etc/fstab"]++后值为1。对第二个# /etc/fstab行进行处理时,arr[$0]即arr["# /etc/fstab"],由于数组中已有此元素,则不在重新赋值,arr["# /etc/fstab"]值为1,对其取反,!arr["# /etc/fstab"]=0,关系表达式为假,则本行不再打印。

然后,由于++,arr["# /etc/fstab"]的值则为2。

对第三# /etc/fstab行进行处理,取反后依然为假,不输出。

结果就是,awk '!arr[$0]++'后跟文件,会把重复的行过滤掉,只打印非重复的行。