本文部分翻译自这里,来自 Jadu Saikia 的博客,这个博客上有很多非常有用的小技巧,有空可以多看看。
通常如果我们想获取一个文件里不重复的行的时候,我们可以直接通过
sort -u
命令,先把文件排序,然后去掉连续的重复行就行。
可是,如果我们去掉重复行之后,还想保留文件原有的顺序,该怎么办呢?虽然
Linux 下有个看上去似乎很有用的命令叫
uniq
,但事实上 uniq
命令仅仅只对连续的重复行有效。譬如我们有这样一个文件:
如果不排序,直接使用 uniq 命令是没有用的:
使用 sort -u
的话,我们就丢失了文件原有的行的顺序了:
sort
和 uniq
一起用,和
sort -u
效果是一样的:
一个终极的解决方案是使用 awk:
简要解释一下,awk 的基本执行流程是,对文件的每一行,做一个指定的逻辑判断,如果逻辑判断成立,则执行指定的命令;如果逻辑判断不成立,则直接跳过这一行。
我们这里写的 awk 命令是
!x[$0]++
,意思是,首先创建一个 map 叫
x
,然后用当前行的全文 $0
作为 map
的 key,到 map 中查找相应的
value,如果没找到,则整个表达式的值为真,可以执行之后的语句;如果找到了,则表达式的值为假,跳过这一行。由于表达式之后有
++
,因此如果某个 key 找不到对应的 value,该
++
操作会先把对应的 value 设成
0,然后再自增成
1,这样下次再遇到重复的行的时候,对应的 key
就能找到一个非 0 的 value 了。
我们前面说过,awk 的流程是先判断表达式,表达式为真的时候就执行语句,可是我们前面写的这个 awk 命令里只有表达式,没有语句,那我们执行什么呢?原来,当语句被省略的时候,awk 就执行默认的语句,即打印整个完整的当前行。就这样,我们通过这个非常简短的 awk 命令实现了去除重复行并保留原有文件顺序的功能。