白日依山尽,黄河入海流。欲穷千里目,更上一层楼。 -- 唐·王之涣

一次awk疑惑分析

今天在做数据分析的时候,需要对原始文件做排重处理,网上看到一个方式如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root@localhost cc]# cat 2.txt
adc 3 5
a d a
a 3 adf
a d b
a 3 adf

去重第一列重复的行:

[root@localhost cc]# cat 2.txt |awk ‘!a[$1]++{print}‘
adc 3 5
a d a

重复的行取最上面一行记录

去重以第一列和第二列重复的行:

[root@localhost cc]# cat 2.txt |awk ‘!a[$1" "$2]++{print}‘
adc 3 5
a d a
a 3 adf

但是按照awk模式理解,上面的语法是没有用到BEGINEND, 那就是主体block, 但是主体block 的语法为 /pattern/ {command} ,有感觉不符合


分析

1、上面语句中的{print} 默认是打印所有符合条件的列, 是 print $0 的简写,

2、a[$1]++ 拆分开来看,a[$1] 是把第一列作为数组下标,也就是 a[$1] 的结果是个数字 这样才能执行后面的++ 操作

3、既然如此,那就改造语句执行如下

1
2
3
4
5
6
7
8
9
10
11
root@pts/0 $ cat repeat.txt |awk '{print a[$1]++, $0}'
0 adc 3 5
0 a d a
1 a 3 adf
2 a d b
3 a 3 adf

root@pts/0 $ cat repeat.txt |awk 'a[$1]++{print}'
a 3 adf
a d b
a 3 adf

4、从上面的对比结果发现,非0的结果被输出,都是第一列出现重复的,也就是存在 条件判断 ? 做以下改造验证

1
2
3
4
root@pts/0 $ cat repeat2.txt |awk '{if (a[$1]++ > 0)print}'
a 3 adf
a d b
a 3 adf

和第3步的结果对比发现,确实做了条件判断

5、所以问题现在定位在两个方面,最主要的是数组中第一次出现的元素的值为何是0

6、进过排查,针对awk数组,有个特性是:

1、直接引用一个数组中不存在的元素时,awk会自动创建这个元素,并且为其赋值为"空字符串"

2、awk中,当变量a的值为字符串时,也可以进行加法运算,如果字符串参与运算,字符串将被当做数字0进行运算

3、空字符串也是字符串,参与运算时,也会被当做数字0进行运算

验证:

直接引用一个数组中不存在的元素时,awk会自动创建这个元素,并且为其赋值为”空字符串”

awk-not-exist-default-null.png

awk中,当变量a的值为字符串时,也可以进行加法运算,如果字符串参与运算,字符串将被当做数字0进行运算

awk-str-compute-as-zero.png

空字符串也是字符串,参与运算时,也会被当做数字0进行运算

awk-str-not-exist-and-compute.png

综上所述

1、条件判断是可以放到主体block{} 外面的
2、a[$1]++ 遇到第一个出现的记录时,其实就是数组中不存在的一个元素,其默认是为空,计算是被当做0

所以上面的最上面例子中的语句等价于

1
2
3
4
5
6
7
8
9
root@pts/0 $ awk 'a[$1]++>0{print}' repeat.txt
a 3 adf
a d b
a 3 adf

root@pts/0 $ awk '{if(a[$1]++>0) print}' repeat.txt
a 3 adf
a d b
a 3 adf

refer:
[1] http://www.mamicode.com/info-detail-1616775.html
[2] https://www.jianshu.com/p/cae3cccd2ee6

作者

Colin

发布于

2020-08-28

许可协议