【问题标题】:Project TSV file columns by name with AWK使用 AWK 按名称投影 TSV 文件列
【发布时间】:2016-12-07 18:44:02
【问题描述】:

我正在尝试按列名投影 TSV 文件。到目前为止,我从一个关于 SO 的问题中获得灵感,我正在使用这个脚本,t.awk

BEGIN {
    OFS="\t"
    split(cols,out,",")
}
NR==1 {
    for (i=1; i<=NF; i++)
        ix[$i] = i
}
NR>1 {
    for (i=1;i<=length( out);i++)
        printf "%s%s", $ix[out[i]], OFS
    print ""
}

我可以这样调用:

awk -f t.awk -v cols=name1,name2,nameN input.tsv

它工作得很好,除非cols 属性指定的名称之一不存在。

如何修改它以使其在这种情况下也能正常工作?我希望它只是忽略作为参数传递的任何不存在的列名。

示例(编辑): 考虑 input.tsv:

a    b    c
1    2    3
2    3    4
5    6    7

我想要命令:

awk -f t.awk -v cols=a,c,batman input.tsv

生产:

a    c
1    3
2    4
5    7

但是,到目前为止,它给出了:

awk: illegal field $(), name "batman"

【问题讨论】:

  • 您能否提供一个示例数据,让我们知道什么是不工作的?
  • 您使用的是哪个awk 版本? GNU Awk 4.1.3 为示例调用生成一些随机输出。
  • 目前我在 MacOS 上,它报告:awk 版本 20070501

标签: bash csv awk


【解决方案1】:

你可以这样调整你的 awk 脚本:

BEGIN {
    OFS="\t"
    split(cols, out, ",")
    for (i in out)
       c[out[i]]
}
NR==1 {
    for (i=1; i<=NF; i++)
       if ($i in c)
          hdr[i] = $i
}
{
    k=0
    for (i=1; i<=NF; i++)
        if (i in hdr)
           printf "%s%s", (k++?OFS:""), $i
    print ""
}

然后运行如下:

awk -f t.awk -v cols=a,c,batman input.tsv

或:

awk -f t.awk -v cols=c,batman,a input.tsv

两者都会产生这个输出:

a   c
1   3
2   4
5   7

【讨论】:

    【解决方案2】:

    这是一个更简单的重写

    $ awk -v cols='a,c,x' -v d=',' 'NR==1 {for(i=1;i<=NF;i++) if(d cols d ~ d $i d) ix[i]} 
                                          {for(i in ix) printf "%s", $i OFS;
                                           print ""}' file
    
    a c
    1 3
    2 4
    5 7
    

    但可能不会保留列顺序。

    【讨论】:

      【解决方案3】:

      这样做:

      BEGIN {
          FS=OFS="\t"
          numIdxs = split(cols,idx2name,/,/)
      }
      NR==1 {
          for (fldNr=1; fldNr<=NF; fldNr++) {
              name2nr[$fldNr] = fldNr
          }
          next
      }
      {
          for (idx=1; idx<=numIdxs; idx++) {
              name = idx2name[idx]
              if (name in name2nr) {
                  fldNr  = name2nr[name]
                  fldVal = $(name2nr[fldNr])
                  printf "%s%s", (numPrinted++ ? OFS : ""), fldVal
              }
          }
      }
      numPrinted { print ""; numPrinted=0 }
      

      相信我,当你遇到下雨天的情况时,如果你以后必须回顾和加强它,你会更快乐。

      请注意,与您的原始脚本一样,以上将按照您在命令行中指定的顺序而不是它们在输入文件中出现的顺序输出字段,因此您可以根据需要从命令行重新排列列。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-01-13
        • 1970-01-01
        • 2012-01-07
        • 2019-12-05
        • 1970-01-01
        • 2013-10-11
        • 1970-01-01
        • 2013-03-04
        相关资源
        最近更新 更多