我有一个文件A.txt
(sep = \t
):
Cycle Well Value Target
1 A1 5.07368111264623 EC
1 A1 3.06982862746599 FT
1 A1 2.46545646544623 EC
和第二个文件B.txt
(sep = \t
,第一列为空):
Well Fluor Target Content Sample
A1 Cy5 EC Unkn-01 2060563935
A1 Cy5 FT Unkn-09 2156515156
Content
我想添加来自B.txt
in 的列A.txt
我想添加if两个都 Well
和Target
是两个文件中的相同数据,并将结果输出到C.txt
(sep = \t
):
Cycle Well Value Target Content
1 A1 5.07368111264623 EC Unkn-01
1 A1 3.06982862746599 FT Unkn-09
1 A1 2.46545646544623 EC Unkn-01
我尝试类似的东西:
awk -F"\t" 'FNR==NR{if (a[$2]) {a[$2]=a[$2] "\t" $7} else {a[$2]=$7}} NR>FNR{split($0,f,"\t"); if (a[f[4]]) $0=$0 "\t" a[f[4]]; print}'
但这没有用。知道如何做到这一点吗?
精确 :
- 在用作模板的第一个文件 (A.txt) 中,存在具有相同孔和目标的多个木质素。
- 在 B.txt 中,只有一行具有相同的孔/目标组合。
- 文件 A 和文件 B 中不可能没有匹配的模式。
答案1
第一个解决方案使用GNU awk或者POSIX awk
编辑:作为埃德·莫顿在他的评论中写道,最初的答案关于awk
仅受 GNU 支持的内容是错误的。 (GNU 文档与 POSIX 文档中的措辞有点令人困惑。)
GNUawk
文档中的称呼多维数组支持 POSIX 兼容awk
。看https://pubs.opengroup.org/onlinepubs/000095399/utilities/awk.html并搜索“多维”或SUBSEP
。这些数组实际上是一维的。
GNUawk
还支持数组的数组这是真正的多维数组。
此版本的命令需要 GNU awk
:
awk -F"\t" 'NR == FNR { a[$2][$4] = $5; next } { print $0, a[$2][$4] }' B.txt A.txt > C.txt
POSIX 兼容变体 (*),应该与任何awk
兼容
awk -F"\t" 'NR == FNR { a[$2,$4] = $5; next } { print $0, a[$2,$4] }' B.txt A.txt > C.txt
两者都打印
Cycle Well Value Target
1 A1 5.07368111264623 EC Unkn-01
1 A1 3.06982862746599 FT Unkn-09
1 A1 2.46545646544623 EC Unkn-01
文件中的数据B.txt
被保存到数组中,a
因为根据问题,键 Well/Target 在该文件中是唯一的。然后将此数据附加到 file 中的数据A.txt
。
必须明确指定字段分隔符。否则awk
将忽略空列/值。
该解决方案使用固定列号来标识要匹配或打印的列。
编辑:与上面显示的 POSIX 兼容解决方案 (*) 相比,以下将索引表达式与\t
分隔符显式组合的解决方案没有任何优势。
awk -F"\t" 'NR == FNR { a[$2 "\t" $4] = $5; next } { print $0, a[$2 "\t" $4] }' B.txt A.txt > C.txt
这相当于设置SUBSEP = "\t"
和使用语法a[$2, $4]
。
第二种解决方案使用q
工具 q 可用于对 CSV 文件执行类似数据库的查询。
看http://harelba.github.io/q/或者https://github.com/harelba/q
该解决方案存在以下问题: 中的列标题为空B.txt
。作为解决方法,我Empty
向该文件的标题行添加了一个标题。
所以我使用这些文件:
A.txt
Cycle Well Value Target
1 A1 5.07368111264623 EC
1 A1 3.06982862746599 FT
1 A1 2.46545646544623 EC
B.txt
Empty Well Fluor Target Content Sample
A1 Cy5 EC Unkn-01 2060563935
A1 Cy5 FT Unkn-09 2156515156
命令
q -H -t "select a.Cycle,a.Well,a.Value,a.Target,b.Content from A.txt as a inner join B.txt as b on a.Well=b.Well and a.Target=b.Target"
印刷
1 A1 5.07368111264623 EC Unkn-01
1 A1 3.06982862746599 FT Unkn-09
1 A1 2.46545646544623 EC Unkn-01
要打印标题,您可以添加printf
orecho
命令。
printf "Cycle\tWell\tValue\tTarget\tContent\n" > C.txt
q -H -t "select a.Cycle,a.Well,a.Value,a.Target,b.Content from A.txt as a inner join B.txt as b on a.Well=b.Well and a.Target=b.Target" >> C.txt
要自动修改文件,B.txt
您可以使用
printf "Empty" > B1.txt
cat B.txt >> B1.txt
printf "Cycle\tWell\tValue\tTarget\tContent\n" > C.txt
q -H -t "select a.Cycle,a.Well,a.Value,a.Target,b.Content from A.txt as a inner join B1.txt as b on a.Well=b.Well and a.Target=b.Target" >> C.txt
该解决方案使用标题行中的命名列来标识要匹配或打印的列。
答案2
假设您有awk
用于二维数组的 GNU,以下程序将完成该任务:
awk -F'\t' 'NR==FNR&&FNR>1{map[$2][$4]=$5}\
NR>FNR{if (FNR==1) {$5="Content"} else {$5=map[$2][$4]}} NR>FNR' B.txt A.txt > C.txt
这将首先处理B.txt
创建“内容”值到特定“井”和“目标”组合的映射。之后进行处理时A.txt
(由 表示FNR
,每个文件行计数器现在小于NR
全局行计数器),程序会在当前行上查找“Well”和“Target”的特定组合,并替换相应的“先前创建的地图中的“内容”值。它仅在处理第二个文件(“杂散”NR>FNR
情况)后打印输出。