![使用 awk/join 基于列连接条目](https://linux22.com/image/129030/%E4%BD%BF%E7%94%A8%20awk%2Fjoin%20%E5%9F%BA%E4%BA%8E%E5%88%97%E8%BF%9E%E6%8E%A5%E6%9D%A1%E7%9B%AE.png)
我有两个用竖线分隔的文件,并且两个文件中的第 1 列+第 2 列可能匹配,或者一个文件可能包含该条目,而另一个文件则没有。假设我要使用管道 '|' 得出的匹配键等于 $1"-"$2作为FS。
文件1
1111|AAA|foo|50
1111|BBB|foo|30
2222|BBB|foo|10
文件2
1111|AAA|bar|10
1111|CCC|bar|20
3333|AAA|bar|40
第一个条目所需的输出如下(我有这个工作)
1111|AAA|50|10
对于第二个条目file1(如果两个文件中都没有匹配的column1+column2,则将foo缺少的条目替换为0。反之亦然)
1111|BBB|30|0
对于文件 2 中但不在文件 1 中的条目键(列 1+列 2)(这是文件 2 预期输出的条目 3)
3333|AAA|0|40
因此,所需的输出整体格式是列出两个文件中由 column1+column2 表示的所有唯一键。第三列条目是文件 1 第 4 列中的值(如果文件 1 中不存在值,则为 0),输出中的第四列为文件 2 的第 4 列中的值(如果文件 2 中不存在值,则为 0) )。
我做了很多研究并尝试了很多事情,但如果使用以下命令,如果 file2 中存在 column1+column2 对,但 file1 中不存在,则我的值不会输出:
join -t"|" -e0 -a1 -a2 -o 1.2,1.3,1.5,2.5 <(<file1 awk -F"|" '{print $1"-"$2"|"$0}' | sort -k1,1) <(<file2 awk -F"|" '{print $1"-"$2"|"$0}' | sort -k1,1)
如果 file1 中有一个 column1+column2 匹配但 file2 中没有,上面的情况会给出预期的输出,并为不存在的匹配附加 0...我怎样才能让它适用于所有场景?
上面的命令将通过在两个文件的第 1 列(column1+column2)中添加一个键来进行一些过程替换,然后基于该新键进行连接。 -e0 如果该键存在于 file1 但不存在于 file2 中,则会添加 0。我怎样才能让它涵盖以下情况:新密钥(column1-column2)存在于文件 2 但不存在于文件 1 中?
答案1
按照你的方法,你必须使用join
两次(或改变你的方法,通过一次join
调用来完成它):
- 打印公共线和不可配对的
file1
线join -t'|' -e0 -a1 -o 1.2,1.3,1.5,2.5 <(<file1 awk -F'|' '{print $1"-"$2"|"$0}' | sort -t'|' -k1,1) <(<file2 awk -F'|' '{print $1"-"$2"|"$0}' | sort -t'|' -k1,1)
- 打印不可配对的
file2
行join -t'|' -e0 -v2 -o 2.2,2.3,1.5,2.5 <(<file1 awk -F'|' '{print $1"-"$2"|"$0}' | sort -t'|' -k1,1) <(<file2 awk -F'|' '{print $1"-"$2"|"$0}' | sort -t'|' -k1,1)
您可以通过一次awk
调用执行相同的操作,存储$4
在由 eg 索引的两个数组中$1|$2
,然后在END
块中迭代每个数组索引,比较它们并相应地打印:
awk -F'|' 'NR==FNR{z[$1"|"$2]=$4;next}{x[$1"|"$2]=$4}
END{for (j in x){if (!(j in z)){print j, "0", x[j]}};
for (i in z){if (i in x){print i, z[i], x[i]} else {print i, z[i], "0"}}
}' OFS="|" file1 file2
答案2
以下将|
两个文件中的第一个替换为@
(使用文件中其他地方未出现的字符),执行join
,然后将 更改回@
原始|
。这样,我们就创建了一个新的|
分隔连接字段,其中包含原始文件中的第 1 列和第 2 列。
join -t'|' -e0 -a1 -a2 -o0,1.3,2.3 \
<( sed 's/|/@/' file1 | sort ) \
<( sed 's/|/@/' file2 | sort ) |
tr '@' '|'
在输出字段规范 ( -o
) 中,零表示连接字段,任一文件中的第 3 列实际上是原始数据的第 4 列。
对于给定的输入文件,这会生成
1111|AAA|50|10
1111|BBB|30|0
1111|CCC|0|20
2222|BBB|10|0
3333|AAA|0|40
答案3
另一种awk
方法:
awk -F'|' 'NR==FNR{f1[$1FS$2]=$NF;next} {f2[$1FS$2]=$NF}
END{for (x in f1){print x,f1[x],f2[x]?f2[x]:0; delete f2[x]};
for (y in f2) print y, 0, f2[y]
}' file[12] OFS='|'
解释:
NR==FNR{f1[$1FS$2]=$NF;next}
,这将仅针对 file1 运行,并且使用组合键将在名为(将替换为awk 的数组中$1FS$2
存储最后一列值$NF
f1
FS
|
F产量S运算符)。{f2[$1FS$2]=$NF}
,与上面相同,但这只会针对 file2 运行for (x in f1){print x,f1[x],f2[x]?f2[x]:0; delete f2[x]}
,在数组中循环f1
并打印 key (x
),它在 file1 中的值f1[x]
,如果 file2 中存在相同的 file1 key,则也打印它,否则打印0
(使用三元条件f2[x]?f2[x]:0
),之后我们还从 file2 中删除相同 key 的记录delete f2[x]
。for (y in f2) print y, 0, f2[y]
,现在 arrayf2
包含仅存在于 file2 中的记录,因此我们打印它们的键 (y
),0
因为它们不存在于 file1 中,并且它们的值存在于 file2 中f2[y]
。