使用 awk 命令按行的第一列取两个文件的交集

注意,仅能处理第一列没有重复数据的情况。

场景

假设有如下两个文件,两列之间按空格分割:

file.base.txt

1
2
3
4
aaa 1
bbb 2
ccc 3
ddd 4

file.test.txt

1
2
3
4
ddd 6
ggg 7
bbb 8
aaa 9

需要按照第一列取这两个文件的交集,生成新的两份文件,目标结果如下:

file.parsed.base.txt

1
2
3
aaa 1
bbb 2
ddd 4

file.parsed.test.txt

1
2
3
aaa 9
bbb 8
ddd 6

主要的应用场景是,在测试时,比较基线和策略系统产出数据的 diff。


awk 命令样例

最易懂的方式是写一个 python 脚本,读取两个文件,把第一份文件的内容存内存 map 中,key 为第一列的值,value 为行,然后用第二份文件的第一列去匹配 map 。这个执行逻辑通过 awk 命令也可以实现,指令如下

1
2
3
awk 'NR==FNR{a[$1]=$0;next}NR>FNR{if($1 in a)print $0"\n"a[$1]}' file.test.txt file.base.txt > file.tmp
awk 'NR%2 == 0' file.tmp | sort -k1 > file.parsed.test.txt
awk 'NR%2 == 1' file.tmp | sort -k1 > file.parsed.base.txt

对于第一个 awk 表达式中的命令,解释如下(版权归属 gpt):

  • NR==FNR{a[$1]=$0;next} :在处理第一个文件(file.test.txt)时,NR(当前处理的总行数)和FNR(当前文件的行数)是相等的。这个表达式的作用是创建一个数组 a,索引是每行的第一列的值 $1,值是整行内容 $0next 命令则使awk跳过后面的处理,直接开始处理下一行。

  • NR>FNR{if($1 in a)print $0"\n"a[$1]} :当处理第二个(file.base.txt)和后续文件时,NR 将大于 FNR。这个表达式的作用是检查当前行的第一列的值 $1 是否在数组 a 的索引中。如果在,就打印当前行 $0,然后打印一个换行符 \n,接着打印数组 a 中与 $1 对应的值 a[$1](即第一个文件中相应行的内容)。

此时 file.tmp 中偶数行存的是过滤后的 test 数据,奇数行存的是过滤后的 base 数据,后面两个命令就是将这两个部分数据提取出来,然后再按照第一列排个序。


如果有重复数据

假如两份原始文件中第一列存在重复的数据,那么执行就会有问题。根据之前的分析,file.test.txt 中的第一列会作为 key 存到 a 中,那么如果有重复数据,a 中存的就是重复数据最后一个的值,也即最终的结果里,file.test.txt 的数据会有问题,样例如下

file.base.txt

1
2
3
4
5
aaa	1
bbb 2
aaa 4
bbb 5
aaa 6

file.test.txt

1
2
3
4
5
aaa	7
bbb 8
aaa 9
bbb 10
aaa 11

处理的结果为:

file.parsed.base.txt

1
2
3
4
5
aaa	1
aaa 4
aaa 6
bbb 2
bbb 5

file.parsed.test.txt

1
2
3
4
5
aaa	11
aaa 11
aaa 11
bbb 10
bbb 10

暂时还没想到好的办法处理