使用AWK按名称计划TSV文件列

时间:2016-12-07 18:44:03

标签: bash csv awk

我正在尝试按列名投影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"

3 个答案:

答案 0 :(得分:1)

这是一个更简单的重写

$ 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

列顺序可能不会被保留。

答案 1 :(得分: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 :(得分:0)

这样做:

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 }

相信我,当你遇到下雨天的情况时,如果你不得不回顾并稍后加强它,你会更开心。

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