生物信息学

【awk】 '!a[$0]++'去重原理分析

2019-01-22  本文已影响48人  caokai001

原文

很多人都知道awk '!a[$0]++' file可以去除文本中重复的行,但是对其到底是如何去重的却不是很清楚,所以这里就单独来分析一下这个命令。

首先我们要知道这条命令是隐含了一个print $0的,完整命令如下:

$ cat c
1
2
2
3
1
3
3

$ awk '!a[$0]++{print $0}' c
1
2
3

awk判定以下三种情况为“假”,其他情况都为“真”:

数字0
空字符串""
未定义的变量,对于未定义的变量var,如果要进行字符串操作,会被转成空字符串"",如果要进行数学运算,会被转成数字0,也可以使用var""来强制转为空字符串""var+0来强制转为数字0
所以上面这条awk语句的原理就是判断!a[$0]++的值,如果值为真就打印当前行,值为假自然就不会打印当前行了。

开始分析!a[$0]++吧,不过还得先看一下awk中操作符的优先级(由高到低排列):

$                #字段引用($1,$2)
++ --
^ **             #求幂
+ - !            #正、负、逻辑非       
* / %
+ -              #加法减法
(blank)          #连接符
< <= == != > >=
~ !~ 
&&
||
?:   
= += -= *= /= %= ^= **=    
这里看到++操作符优先级是高于!操作符的。
现在正式开始分析!a[$0]++,先使用{print ">"a[$0]+0}来输出每次进行!a[$0]++计算后a[$0]的值:
$ echo -e "5\n5\n5"|awk '{print ">"a[$0]+0}!a[$0]++{print $0}'
>0
5
>1
>2
为了简化输出,这里只对相同的三行进行去重,可以看到处理第一行之前a[$0]还是未定义的,
所以输出为空(这里通过a[$0]+0强制转换成了0),当第一行处理完成之后a[$0]的值变成了1,
第二行处理完成之后a[$0]的值变成了2,以此类推。

这里我们可以将a[$0]数组取值替换为一个简单的变量,方便理解:

$ awk 'BEGIN{a=0;print !a++,a}'
1 1
$ awk 'BEGIN{a=1;print !a++,a}'
0 2
$ awk 'BEGIN{a=2;print !a++,a}'
0 3

我们知道a++操作符是在变量a使用完之后再对变量进行自增,所以这里虽然++比!优先级高,先跟变量a结合,但是不会立即自增变量a的值,而是在!a之后在自增变量a的值。通过例子来理解:

变量a的值为0,`!a`取反后返回1,然后`a++`将变量a的值自增1,此时`!a++`返回的值就是`!a`计算出来的1,同时变量a的值也变为了1
变量a的值为1,`!a`取反后返回0,然后`a++`将变量a的值自增1,此时`!a++`返回的值就是`!a`计算出来的0,同时变量a的值变为2
变量a的值为2,`!a`取反后返回0,然后`a++`将变量a的值自增1,此时`!a++`返回的值就是`!a`计算出来的0,同时变量a的值变为3

现在是不是一目了然了,再代入回之前的例子中:

$ echo -e "5\n5\n5"|awk '{print ">"a[$0]+0}!a[$0]++{print $0}'
>0
5
>1
>2

分析如下:

开始处理第一行之前,a[$0]是未定义的,所以值为空(相当于上面的a=0),!a[$0]取反返回1,然后a[$0]++a[$0]的值自增1,此时![$0]++返回的值就是!a[$0]计算出来的1,数字1在awk中为真,所以会执行后面的{print $0}输出第一行,同时a[$0]的值也变为了1
开始处理第二行之前,a[$0]值为1(相当于上面的a=1),!a[$0]取反返回0,然后a[$0]++a[$0]的值自增1,此时![$0]++返回的值就是!a[$0]计算出来的0,数字0在awk中为假,所以不会执行后面的{print $0}来输出第二行,同时a[$0]的值变为2
开始处理第三行之前,a[$0]值为2(相当于上面的a=2),!a[$0]取反返回0,然后a[$0]++a[$0]的值自增1,此时![$0]++返回的值就是!a[$0]计算出来的0,数字0在awk中为假,所以不会执行后面的{print $0}来输出第三行,同时a[$0]的值变为3
分析完成,不过这只是我个人的理解,有不对的地方请回复指出,一起学习!

上一篇 下一篇

猜你喜欢

热点阅读