Awk:匹配字段范围并进行匹配异常

时间:2017-11-22 00:35:41

标签: awk string-matching

我的文件的子集如下所示:

row1 ./. 1/1 1/1 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 1/1 0/0 0/0 0/0 0/0 ./. 1/1 0/0 0/0
row2 ./. 0/0 0/0 0/0 0/0 0/0 0/0 0/0 ./. 0/0 0/0 0/0 ./. 0/0 0/0 0/0 0/0 0/0 ./. 0/0 ./. ./.
row3 ./. 0/1 0/0 0/0 0/0 1/2 5/6 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 ./. 1/1 0/0 0/0
row4 ./. 1/1 1/1 0/0 0/0 0/0 0/0 1/6 0/0 ./. 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 ./. 1/1 0/1 0/0
row5 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0 0/0

值格式为n / n,其中n可以等于数字(0-9)或句点(。)

我的目标: 如果第2,3,4,20和21列相同,则返回1,否则返回0.如果列相同,则包含“./。”的列。被忽略,返回1,否则返回0。

示例所需输出:

row1 0
row2 1
row3 0
row4 0
row5 1

第二行收到“1”,因为虽然有一些“./。”的实例。在我想要比较的列中,感兴趣的列中的所有其他值都是相同的。第五行收到“1”,因为感兴趣的列中的所有值都相同。

我写了这个,这部分地做了我想要的(它不包括所有必要的字段组合):

awk 'BEGIN{OFS=" "}{sign="";{if ( ($2==$3 || $2=="./." || $3=="./.") && ($2==$4 || $4=="./.") && ($2==$20 || $20=="./.") && ($2==$21 || $21=="./.")) {sign="1 "}else{sign="0 "}}; print $2, $3, $4, $20, $21, sign}'test.txt

我的全尺寸文件有更多列需要包含在匹配中;有没有更简洁的方式来写这个?

1 个答案:

答案 0 :(得分:2)

$ awk 'BEGIN{a[2];a[3];a[4];a[5];a[21]} {for (i in a) if ($i!="./.") b[$i]; print $1,(length(b)==1); delete b}' test.txt 
row1 0
row2 1
row3 0
row4 0
row5 1

或者,对于喜欢将代码分布在多行中的人来说:

awk '
  BEGIN{
    a[2];a[3];a[4];a[5];a[21]
  }

  {
    for (i in a)
      if ($i!="./.")
        b[$i]
    print $1,(length(b)==1)
    delete b
  }' test.txt 

如何运作

  1. 数组a确定我们检查的列:

    a[2];a[3];a[4];a[5];a[21]
    

    这将用于表示感兴趣的是2,3,4,5和21列。

  2. 只要列值不是b,我们就会为a定义的列的每个不同值为数组./.分配一个键:

    for (i in a) if ($i!="./.") b[$i]`
    
  3. 我们打印出结果:

    print $1,(length(b)==1)
    

    如果b的长度为1,则表示感兴趣的列(不包括./.列)都具有相同的值。在这种情况下,我们打印行标题和1.如果它的长度不同于1,我们打印行标题和0。

  4. 最后,我们删除b以准备分析下一行:

    delete b
    
  5. 定义感兴趣列的替代方法

    $ awk -v x='2 3 4 5 21' 'BEGIN{split(x,a)} {for (i in a) if ($a[i]!="./.") b[$a[i]]; print $1,(length(b)==1); delete b}' test.txt 
    row1 0
    row2 1
    row3 0
    row4 0
    row5 1