【问题标题】:match pattern and print corresponding columns from a file using awk or grep匹配模式并使用 awk 或 grep 从文件中打印相应的列
【发布时间】:2016-12-28 03:23:48
【问题描述】:

我有一个带有重复标题的输入文件(如下):

A1BG A1BG A1CF A1CF A2ML1
aa bb cc dd ee
1 2 3 4 5

我想在一个文件中打印具有相同标题的所有列。例如,对于上述文件,应该有三个输出文件; 1 个用于 A1BG,2 列; 2 列的 A1CF 排名第二; 1 列的 A2ML1 排名第 3。我有什么方法可以通过 awk 或 grep 使用单行代码吗?

我尝试了以下单行:

awk -v f="A1BG" '!o{for(x=1;x<=NF;x++)if($x==f){o=1;next}}o{print $x}' trial.txt

但这仅在一列中搜索模式(在本例中为 1)。我想查看所有标题名称并打印标题中包含 A1BG 的所有相应列。

【问题讨论】:

  • 你为什么需要它成为一个单行字?你的换行符用完了吗?如果您不要求单行,那么您将在此论坛中获得更多热情的帮助,因为这只会让每个人都畏缩,然后您可以将获得的任何格式良好、可读的脚本塞进一行,如果出于某种原因,您觉得这对您有益。

标签: bash awk sed grep


【解决方案1】:

我无法为您提供 1-liner 但这里是 GNU awk 的 10-liner:

script.awk

  NR == 1 { PROCINFO["sorted_in"] = "@ind_num_asc"
            for( i=1; i<=NF; i++ ) { f2c[$i] = (i==1)? i : f2c[$i] " " i } }
        { for( n in f2c ) { 
              split( f2c[n], fls, " ")
              tmp = ""
              for( f in fls ) tmp = (f ==1) ? $fls[f] : tmp "\t" $fls[f]
              print tmp > n
          }
        }

像这样使用它:awk -f script.awk your_file

在第一个动作中:它根据第一条记录 (NR == 1) 中的列确定文件名。

在第二个动作中:对于每个记录:对于每个输出文件:其列(如第一个记录中定义)被收集到 tmp 并写入输出文件。

使用 PROCINFO 需要 GNU awk,请参阅 Ed Mortons cmets 了解替代方案。

示例运行和输出:

> awk -f mpapccfaf.awk mpapccfaf.csv 
> cat A1BG 
A1BG    A1BG
aa      bb
1       2

【讨论】:

  • 这并没有给我想要的输出。我需要在一个文件中打印具有相同标题的所有列(例如,对于 A1BG,一个文件应该有两个完整的列)。我提到的一个班轮可以完成工作,但它只是检查指定的列(x = 1)。我想修改它,以便它查看完整的标题行并打印所有在其标题中包含 A1BG 的列。谢谢
  • @aan 请为从示例输入派生的文件添加示例输出(期望的输出)。如果您想要文件中的标题:删除 next
  • 输入上面的表格,A1BG 的所需输出是:制表符分隔文件(2 列 n 3 行)A1BG A1BG ab bg 1 2 与其他标签类似。
  • @EdMorton 感谢您的 cmets。我更改了对于我的 gnu awk 版本和给定的数据,有或没有 procinfo 语句没有区别。
  • @LarsFischer 似乎在代码中限制了它打印(或查找)每个模式仅 3 个条目。只要条目为 3 或小于 3,它就可以正常工作。对于重复超过 3 次的模式,它只打印前 3 列并跳过休息以寻找第二个重复模式。我无法弄清楚..您能否建议对您的脚本进行一些更改。
【解决方案2】:

awk 解决方案应该很快 - 输出文件以制表符分隔并命名为 cols.A1BG cols.A1CF 等

awk '
# fill cols columns map to header and tab map to track tab state per header
NR==1 {
  for(i=1; i<=NF; ++i) {
    cols[i]=$i
    tab[$i]=0
  }
}
{
# reset tab state for every header
  for(h in tab) tab[h]=0
# write tab-delimited column to its cols.header file
  for(i=1; i<=NF; ++i) {
    hdr=cols[i]
    of="cols." hdr
    if(tab[hdr]) {
      printf("\t") >of
    } else
      tab[hdr]=1
    printf("%s", $i) >of
  }
# newline for every header file
  for(h in tab) {
    of="cols." h
    printf("\n") >of
  }
}
'

这是我的两个 awk 解决方案的输出:

$ ./scr.sh <in.txt; head cols.*
==> cols.A1BG <==
A1BG    A1BG
aa      bb
1       2

==> cols.A1CF <==
A1CF    A1CF
cc      dd
3       4

==> cols.A2ML1 <==
A2ML1
ee
5

【讨论】:

    【解决方案3】:

    此 awk 解决方案采用与 Lars 相同的方法,但使用 gawk 4.0 2D 数组

    awk '
    # fill cols map of header to its list of columns
    NR==1 {
      for(i=1; i<=NF; ++i) {
        if(!($i in cols))
          j=0
        cols[$i][j++]=i
      }
    }
    {
    # write tab-delimited columns for each header to its cols.header file
      for(h in cols) {
        of="cols."h
        for(i=0; i < length(cols[h]); ++i) {
          if(i > 0) printf("\t") >of
          printf("%s", $cols[h][i]) >of
        }
        printf("\n") >of
      }
    }
    '
    

    【讨论】:

    • 从处理第一行到打印标题到输出文件中删除了下一个
    • 按照惯例,由所有 awk 函数等填充,awk 数组索引和字段编号以及字符串索引都从 1 开始,而不是 0。
    【解决方案4】:

    来吧,按要求提供单线:

    awk 'NR==1{for(i=1;i<=NF;i++)a[$i][i]}{PROCINFO["sorted_in"]="@ind_num_asc";for(n in a){c=0;for(f in a[n])printf"%s%s",(c++?OFS:""),$f>n;print"">n}}' file
    

    上面使用 GNU awk 4.* 来处理真正的多维数组和 sorted_in。

    对于阅读本文的其他人来说,他们更喜欢简洁而不是 OP 需要的简洁性,这里是一个更自然的多行脚本:

    $ cat tst.awk
    NR==1 {
        for (i=1; i<=NF; i++) {
            names2fldNrs[$i][i]
        }
    }
    {
        PROCINFO["sorted_in"] = "@ind_num_asc"
        for (name in names2fldNrs) {
            c = 0
            for (fldNr in names2fldNrs[name]) {
                printf "%s%s", (c++ ? OFS : ""), $fldNr > name
            }
            print "" > name
        }
    }
    
    $ awk -f tst.awk file
    
    $ cat A1BG
    A1BG A1BG
    aa bb
    1 2
    
    $ cat A1CF
    A1CF A1CF
    cc dd
    3 4
    
    $ cat A2ML1
    A2ML1
    ee
    

    【讨论】:

      【解决方案5】:

      由于您在我的另一个答案中的一个 cmets 中写道,您有 20000 列,因此让我们考虑一种两步方法来简化调试以找出哪些步骤中断。

      step1.awk

        NR == 1 { PROCINFO["sorted_in"] = "@ind_num_asc"
                  for( i=1; i<=NF; i++ ) { f2c[$i] = (f2c[$i]=="")? "$" i : (f2c[$i] " $" i) } }
        NR== 2 { for( fn in f2c) printf("%s:%s\n", fn,f2c[fn]) 
                 exit
              }
      

      Step1 应该给我们一个文件列表以及 它们的 列:

      > awk -f step1.awk yourfile
      Mpap_1:$1, $2, $3, $5, $13, $19, $25
      Mpap_2:$4, $6, $8, $12, $14, $16, $20, $22, $26, $28
      Mpap_3:$7, $9, $10, $11, $15, $17, $18, $21, $23, $24, $27, $29, $30
      

      在我的测试数据中,Mpap_1 是第 1、2、3、5、13、19、25 列中的标题。让我们希望这第一步适用于您的大量列。 (坦率地说:我不知道 awk 是否可以处理 $20000。)

      第 2 步:让我们创建一个著名的单衬里:

      > awk -f step1.awk yourfile | awk -F : 'BEGIN {print "{"}; {print "  print " $2, "> \""  $1 "\""  }; END { print "}" }' | awk -v "OFS=\t" -f - yourfile 
      

      第一部分是我们的第 1 步,第二部分即时构建第二个 awk 脚本,行如下:print $1, $2, $3, $5, $13, $19, $25 &gt; "Mpap_1"。第二个 awk 脚本通过管道传送到第三部分,该部分从标准输入 (-f -) 读取脚本并将脚本应用于您的输入文件。

      如果出现问题:观察 step2 的每个部分的输出,您可以执行从左到(但不包括)每个 | 符号的部分并查看发生了什么,例如:

      • awk -f step1.awk yourfile
      • awk -f step1.awk yourfile | awk -F : 'BEGIN {print "{"}; {print " print " $2, "&gt; \"" $1 "\"" }; END { print "}" }'

      【讨论】:

      • 是的,它正在工作。我已经在完整的数据集上运行它,希望能在这么大的文件上工作。但是输出没有分隔符。我需要一个制表符分隔的 .txt 文件作为输出。有可能得到相同的吗?
      • @aan 在最后一部分使用awk -v "OFS=\t" -f - large.csv。 (我会更新我的答案。)
      • 我已经试过了(现在再试一次),但它没有给出一个制表符分隔的输出。我的输入也是一个制表符分隔的 .txt 文件。顺便说一句,我正在使用 GNU Awk 3.1.7 运行它。
      • @aan 如果 -v "OFS=\t" 不起作用(为什么?),您可以将 step1.awk 中的第一个 for 循环 更改为 for( i=1; i&lt;=NF; i++ ) { f2c[$i] = (f2c[$i]=="")? "$" i : (f2c[$i] " \"\\t\" $" i) } 并省略 -v "OFS=\t"在第二步的最后一部分。
      【解决方案6】:

      以下对我有用:

      step1.awk 的代码:

      NR == 1 { PROCINFO["sorted_in"] = "@ind_num_asc" for( i=1; i

      然后运行一个使用上述 awk 脚本的班轮:

      awk -f step1.awk 文件.txt | awk -F : '开始{打印“{”}; {print " print " $2, "> \"" $1".txt" "\"" };结束{打印“}”}'| awk -f -file.txt

      这会输出制表符分隔的 .txt 文件,在一个文件中具有相同标题的所有列。 (每种类型的标头都有单独的文件)

      感谢 Lars Fischer 和其他人。

      干杯

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-08-15
        • 2021-09-24
        • 1970-01-01
        • 1970-01-01
        • 2012-11-23
        • 2011-07-20
        • 2014-03-31
        相关资源
        最近更新 更多