如何在awk中基于列的部分字符串匹配连接两个文件

如何在awk中基于列的部分字符串匹配连接两个文件,awk,Awk,我有两个文件,如下图所示,以制表符分隔: 归档 chr1 123,aa aa b c d chr1 234,dd a b c d chr1 af,345,e aa b c d chr1 456 a b c d .... 文件B xxxx abcd chr1 123,dd aa c d e yyyy defg chr1 aa,345 aa e f g ... 我需要根据fileB中的$4和fileA

我有两个文件,如下图所示,以制表符分隔:

归档

chr1   123,aa  aa b c d
chr1   234,dd  a  b c d
chr1   af,345,e aa b c d
chr1   456 a  b c d
....
文件B

xxxx  abcd    chr1   123,dd    aa    c    d    e
yyyy  defg    chr1   aa,345    aa    e    f    g
...
我需要根据fileB中的$4和fileA中的$2键连接这两个文件,因此输出如下所示:

chr1   123,aa       aa    b    c    d    xxxx    abcd
chr1   234,dd       a     b    c    d    xxxx    abcd
chr1   af,345,e     aa    b    c    d    yyyy    defg
chr1   456          a    b    c    d
我正在尝试使用解决方案:

awk 'NR==FNR{a[$4]=$1OFS$2;next}{$6=a[$2];print}' OFS='\t' fileb filea

只有当fileA中的$2完全匹配时,这才有效。是否有任何解决方案可以匹配fileA的$2中以逗号分隔的字符串?

只需存储fileB中的数据,并在必要时将其连接到fileA中:

awk 'FNR==NR {data[$4]=$1 FS $2; next}
     {
      split($2,a,","); 
      if (a[1] in data) {$0=$0 FS data[a[1]]}
     }1' fb fa
试验
这假设在fileB中不会出现任何给定的$2文件A中的多个值:

$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR==FNR { fileB[$4] = $1 OFS $2; next }
{
    tail = ""
    split($2,fileA,/,/)
    for (i in fileA) {
        if (fileA[i] in fileB) {
            tail = OFS fileB[fileA[i]]
        }
    }
    print $0 tail
}

$ awk -f tst.awk fileB fileA
chr1    123,aa  aa      b       c       d       xxxx    abcd
chr1    234,dd  a       b       c       d
chr1    af,345,e        aa      b       c       d       yyyy    defg
chr1    456     a       b       c       d
考虑到您的新输入/输出,您将需要类似的内容(未经测试):


另一个
awk

$  awk -v c=',' 'NR==FNR{a[c $4 c]=$1 FS $2; next}
                        {for(k in a) if(c $2 c~k) $(NF+1)=a[k]}1' fileB fileA |
column -t


chr1  123,aa    aa  b  c  d  xxxx  abcd
chr1  234,dd    a   b  c  d
chr1  af,345,e  aa  b  c  d  yyyy  defg
chr1  456       a   b  c  d

这里有很多关于专栏的事情。试着更好地解释这一点。如果fileB中的$4与fileA中$2中用逗号分隔的字符串相匹配,那么将fileB中的$1、$2添加到fileA中的$6中。预期输出中的
chr1 123、aa
来自何处?234可能是234,如果我理解正确,它只匹配[1]元素。可能在示例中,它必须与[1]匹配是正确的。但是,在原始文件中,它可以匹配拆分数组“a”中的任何元素,而不仅仅是a[1]。我不应该回答这个问题,因为很明显,您没有提供足够的详细信息;相反,您会不断在评论中添加更多内容。用a编辑你的原始问题。
$(NF+1)
比tail好。这是真的,但它会导致awk重新编译$0,重新评估NF,并重新映射到字段中,因此效率受到影响,NF的结果值将是错误的(
echo'a'| awk'{$(NF+1)=“bc print NF}'
将输出2,而
echo'a'| awk'{tail=“bc;$0=$0的尾迹;打印NF}“
将输出3。现在,文件B中的$4也有用逗号分隔的字符串,并且不使用多个字符串来匹配文件B。我相信,如果您稍微考虑一下,您可能会发现这是一个结果,而无需实际测试。我编辑了我的答案,以满足新的要求。真的吗如果你什么都不懂的话,你可以试着思考一下你得到的解决方案,并问一些问题,因为在给定特定输入集的情况下,生成你期望的输出的脚本只是确定解决方案的起点,而不是当你使用真实数据wrt output、效率、内存、robus运行它时作为YMMV的终点再次抱歉!!OP更新,刚刚意识到fileB中的$4也有逗号分隔的字符串。这种方法在这种情况下不起作用,但您的示例现在是错误的。fileB的
123,dd
应该与fileA中的第1行和第2行匹配。是的!!在这种情况下,它可以添加到fileA中的两行。
BEGIN { FS=OFS="\t" }
NR==FNR {
    split($4,b,/,/)
    for (i in b) {
        fileB[b[i]] = $1 OFS $2
    }
    next
}
{
    tail = ""
    split($2,a,/,/)
    for (i in a) {
        if (a[i] in fileB) {
            tail = OFS fileB[a[i]]
        }
    }
    print $0 tail
}
$  awk -v c=',' 'NR==FNR{a[c $4 c]=$1 FS $2; next}
                        {for(k in a) if(c $2 c~k) $(NF+1)=a[k]}1' fileB fileA |
column -t


chr1  123,aa    aa  b  c  d  xxxx  abcd
chr1  234,dd    a   b  c  d
chr1  af,345,e  aa  b  c  d  yyyy  defg
chr1  456       a   b  c  d