如何通过 awk 匹配两个参数来合并两个文件

如何通过 awk 匹配两个参数来合并两个文件

我有一个文件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.txtin 的列A.txt我想添加if两个都 WellTarget是两个文件中的相同数据,并将结果输出到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

要打印标题,您可以添加printforecho命令。

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情况)后打印输出。

相关内容