比较文件awk,打印匹配并连接如果有多个匹配

时间:2016-09-26 09:35:39

标签: bash awk

您好我有这两个文件:

cat file1.tab
1704 1.000000 T G
1708 1.000000 C G
1711 1.000000 G C
1712 0.989011 T A
1712 0.003564 T G

cat file2.tab
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713

我喜欢这个输出:

1704 1.000000 T G
1705 0
1706 0
1707 0
1708 1.000000 C G
1709 0
1710 0
1711 1.000000 G C
1712 0.003564 T G 0.003564 T G 
1713 0

我几乎可以得到它:

awk 'NR==FNR { a[$1]=$0;b[$1]=$1; next} { if ($1 == b[$1]) print a[$1]; else print $1,"0";}' file1.tab file2.tab

但是我不知道如何处理重复。我的脚本不会检查file1.tab中第1列中的字符是否重复,因此只输出上次显示的$ 0。

4 个答案:

答案 0 :(得分:2)

你可以使用这个awk:

awk 'FNR==NR{a[$1] = (a[$1]==""?"":a[$1] " ") $2 OFS $3 OFS $4; next}
    {print $1, ($1 in a ? a[$1] : 0)}' file1 file2

1704 1.000000 T G
1705 0
1706 0
1707 0
1708 1.000000 C G
1709 0
1710 0
1711 1.000000 G C
1712 0.989011 T A 0.003564 T G
1713 0

参考: Effective AWK Programming 工作原理:

  • FNR==NR - 仅对file1执行此块
  • a[$1] = (a[$1]==""?"":a[$1] " ") $2 OFS $3 OFS $4 - 创建一个关联数组a,其关键字为$1,值为$2 + $3 + $4(继续追加以前的值)
  • next - 跳到下一条记录
  • {...} - 为第二个输入文件file2
  • 执行此块
  • if ($1 in a)如果第二个文件中的$1存在于aray中a
  • print $1, ($1 in a ? a[$1] : 0 - 打印$1以及$1a的数据值,否则0将打印出来。

答案 1 :(得分:2)

您可以使用以下内容:

$ awk 'NR==FNR{$1=$1 in a?a[$1]:$1;$0=$0;a[$1]=$0;next}{print $1 in a?a[$1]:$1 OFS 0}' file1.tab file2.tab
1704 1.000000 T G
1705 0
1706 0
1707 0
1708 1.000000 C G
1709 0
1710 0
1711 1.000000 G C
1712 0.989011 T A 0.003564 T G
1713 0

一些解释如何运作:

  • 对第一个文件执行此块'NR==FNR{$1=$1 in a?a[$1]:$1;$0=$0;a[$1]=$0;next},其中记录索引等于文件记录索引。因此,对于第一个文件,我们将第一个单词设置为存储在数组中的值(如果存在),否则设置为第一个单词。然后,使用$0=$0我们重新分割字段,因为第一个字段现在可能包含多个单词。之后,我们使用第一个单词作为索引
  • 将行存储在数组中
  • 仅对第二个文件的行执行块{print $1 in a?a[$1]:$1 OFS 0}'(由于前一个块中的next语句)。如果我们找到匹配的行,则会打印出来,否则,我们会将0连接到第一个单词,然后打印。

答案 2 :(得分:1)

使用perl

$ perl -F'/\s+/,$_,2' -lane '
    if(!$#ARGV){ $h{$F[0]} .= $h{$F[0]} ? " $F[1]" : $F[1] }
    else{ print "$F[0] ", $h{$F[0]} ? $h{$F[0]} : 0 }
    ' file1.tab file2.tab 
1704 1.000000 T G
1705 0
1706 0
1707 0
1708 1.000000 C G
1709 0
1710 0
1711 1.000000 G C
1712 0.989011 T A 0.003564 T G
1713 0
  • -F'/\s+/,$_,2'在空格上拆分输入行,最多2个字段
  • 对于两个文件命令行参数,
  • !$#ARGV的工作方式类似于awk的NR==FNR
  • %h哈希变量根据第一个字段将附加值保存为键
  • 处理第二个文件时,按所需格式打印
  • -l选项从输入行中删除换行符,并为每个print语句添加换行符

答案 3 :(得分:0)

以下是使用joinuniqtacgrepsort的不可阻挡的思维过程的产物。我们的想法是获得唯一的键值对(特别是对于键1712)并加入它们以避免像1708 1.000000 C G 1.000000 C G这样的行,因此该解决方案不支持为每个键分组三个或更多值。 join -o ... -e "0"也不会在非加入行上生成1 0,因为file1.tab有3列要加入。

$ join -a 1 <(join -a 1 file2.tab <(uniq -w 4 file1.tab )) <(grep -v -f <(uniq -w 4 file1.tab ) <(tac file1.tab|uniq -w 4|sort))
1704 1.000000 T G
1705
1706
1707
1708 1.000000 C G
1709
1710
1711 1.000000 G C
1712 0.989011 T A 0.003564 T G
1713

更结构化的布局:

$ join -a 1 
            <(join -a 1 
                        file2.tab 
                        <(uniq -w 4 file1.tab )) 
            <(grep -v -f 
                         <(uniq -w 4 file1.tab ) 
                         <(tac file1.tab|uniq -w 4|sort))