BASH - 使用Loop和If语句汇总来自独特字段中多个字段的信息

时间:2016-08-26 10:29:46

标签: bash loops if-statement awk multiple-columns

我有以下标签分隔文件:

A1      A1      0       0       2       1       1 1     1 1     1 1     2 1     1 1
A2      A2      0       0       2       1       1 1     1 1     1 1     1 1     1 1
A3      A3      0       0       2       2       1 1     2 2     1 1     1 1     1 1
A5      A5      0       0       2       2       1 1     1 1     1 1     1 2     1 1

我们的想法是总结第7列(包含)与结尾之间的信息,该列是在文件末尾添加的新列。

为此,这些是规则:

  • 如果行中(第7列和结尾之间)的“2”总数为 0 :将“1 1”添加到新的最后一列

  • 如果行中(第7列和结尾之间)的“2”总数 1 :将“1 2”添加到新的最后一列

  • 如果行中(第7列和结尾之间)的“2”总数 2或更多:将“2 2”添加到新的最后一列

我开始使用命令提取我想要处理的列:

  

awk' {for(i = 7; i< = NF; i ++)printf $ i" &#34 ;;打印""}' myfile.ped> tmp_myfile.txt

然后我使用以下方法计算每行中的出现次数:

  

sed' s / [^ 2] // g' tmp_myfile.txtt | awk' {print NR,length}' >   tmp_occurences.txt

哪个输出:

1 1
2 0
3 2
4 1

然后我的想法是编写一个循环遍历行的for循环来添加新的汇总列。 根据我在此处找到的内容,我在想这种结构http://www.thegeekstuff.com/2010/06/bash-if-statement-examples

while read line ;
do
    set $line

    If ["$2"==0]
    then
        $3=="1 1"

    elif ["$2"==1 ]
    then
        $3=="1 2”

    elif ["$2">=2 ]
    then 
        $3==“2 2”

    else
        print ["error"]

    fi
done < tmp_occurences.txt

但我被困在这里。在开始循环之前是否必须创建新列?我正朝着正确的方向前进吗?

理想情况下,最终输出(在合并初始文件和摘要列的前6列之后)将是:

A1      A1      0       0       2       1       1 2
A2      A2      0       0       2       1       1 1
A3      A3      0       0       2       2       2 2
A5      A5      0       0       2       2       1 2 

感谢您的帮助!

4 个答案:

答案 0 :(得分:4)

使用gnu-awk可以:

awk -v OFS='\t' '{
   c=0;
   for (i=7; i<=NF; i++)
      if ($i==2)
         c++
   if (c==0)
      s="1 1"
   else if (c==1)
      s="1 2"
   else
      s="2 2"
   NF=6
   print $0, s
}' file

A1  A1  0   0   2   1   1 2
A2  A2  0   0   2   1   1 1
A3  A3  0   0   2   2   2 2
A5  A5  0   0   2   2   1 2

PS:如果不使用gnu-awk,你可以使用:

awk -v OFS='\t' '{c=0; for (i=7; i<=NF; i++) {if ($i==2) c++; $i=""} if (c==0) s="1 1"; else if (c==1) s="1 2"; else s="2 2"; NF=6; print $0, s}' file

答案 1 :(得分:1)

使用GNU awk为第3个arg匹配():

$ awk '{match($0,/((\S+\s+){6})(.*)/,a); c=gsub(2,2,a[3]); print a[1] (c>1?2:1), (c>0?2:1)}' file
A1      A1      0       0       2       1       1 2
A2      A2      0       0       2       1       1 1
A3      A3      0       0       2       2       2 2
A5      A5      0       0       2       2       1 2

使用其他问题,您可以将\S/\s替换为[^[:space:]]/[[:space:]]并使用substr()代替a[]

答案 2 :(得分:0)

我们可以使用gensub()和捕获组来保持格式:我们捕获6个第一个字段并用它们替换+计算值:

awk '{for (i=7; i<=NF; i++) {
        if ($i==2)
            twos+=1       # count number of 2's from 7th to last field
        }
      f7=1; f8=0          # set 7th and 8th fields's default value
      if (twos)
          f8=2            # set 8th = 2 if sum is > 0
      if (twos>1)
          f7=2            # set 7th = 2 if sum is > 1
      $0=gensub(/^((\S+\s*){6}).*/,"\\1 " f7 FS f8, 1) # perform the replacement
      twos=0              # reset counter
  }1' file

作为一个单行:

$ awk '{for (i=7; i<=NF; i++) {if ($i==2) twos+=1} f7=1; f8=0; if (twos) f8=2; if (twos>1) f7=2; $0=gensub(/^((\S+\s*){6}).*/,"\\1 " f7 FS f8,1); twos=0}1' a
A1      A1      0       0       2       1        1 2
A2      A2      0       0       2       1        1 0
A3      A3      0       0       2       2        2 2
A5      A5      0       0       2       2        1 2

答案 3 :(得分:0)

$ cat > test.awk
{
    for(i=1;i<=NF;i++) {                       # for every field
        if(i<7)                                  
            printf "%s%s", $i,OFS              # only output the first 6 
        else a[$i]++                           # count the values of the of the fields
    }
    print (a[2]>1?"2 2":(a[2]==1?"1 2":"1 1")) # output logic
    delete a                                   # reset a for next record
}
$ awk -f test.awk test
A1 A1 0 0 2 1 1 2
A2 A2 0 0 2 1 1 1
A3 A3 0 0 2 2 2 2
A5 A5 0 0 2 2 1 2

从@ anubhava的解决方案中借用一些想法:

$ cat > another.awk
{
    for(i=7;i<=NF;i++)                             
        a[$i]++                                    # count 2s
    NF=6                                           # truncate $0
    print $0 OFS (a[2]<2?"1 "(a[2]?"2":"1"):"2 2") # print $0 AND 1 AND 1 OR 2 OR 2 AND 2
    delete a                                       # reset a for next record
}