awk:按名称而不是位置

时间:2016-05-30 15:27:30

标签: awk

我有一个逗号分隔的文本文件。第一行是字段名称列表,后续行包含数据。我将获得该文件的新版本,并且我希望按名称而不是列号从特定列中提取所有值。 (即我想要的列可能在文件的不同版本中处于不同的位置。)

例如,这里有两个文件:

foo,bar,interesting,junk
1,2,gold,ramjet
2,25,diamonds,superfluous

foo,bar,baz,interesting,junk,morejunk
5,3,smurf,platinum,garbage,scrap
6,2.5,mushroom,sodium,liverwurst,eew

我喜欢一个单独的脚本,它将通过多个文件,提取有趣的"中的矿物质。柱。 : - )

我到目前为止所获得的是一个文件,但我知道awk比这更优雅。如何清理它并使其一次处理多个文件?

BEGIN {
    FS=",";
}

NR == 1 {
    for(i=1; i<=NF; i++) {
        if($i=="interesting") {
            col=i;
        }
    }
}

NR > 1 {
  print $col;
}

2 个答案:

答案 0 :(得分:2)

你已经非常接近了。只需使用FNR而不是NR,用于&#34;文件NR&#34;。

#!/usr/bin/awk -f

BEGIN { FS="," }

FNR==1 {
  for (col=1;col<=NF;col++)
    if ($col=="interesting")
      next
}

{ print $col }

或者如果您愿意:

#!/usr/bin/awk -f

BEGIN { FS="," }

FNR==1 { for (col=1;$col!="interesting";col++); next }

{ print $col }

或者如果您更喜欢单行:

$ awk -F, -v txt="interesting" 'FNR==1{for(c=1;$c!=txt;c++);next} {print $c}' file1 file2

当然,要小心你实际上有指定的列,否则你可能会发现自己处于无限循环中。您可以找出可以避免这种风险的额外条件。

请注意,在awk中,如果后面跟着另一个命令,则只需要用分号终止命令。因此,你会这样做:

command1; command2

但如果用新行分隔命令,则可以删除分号:

command1
command2

答案 1 :(得分:2)

这样做:

$ cat tst.awk
BEGIN { FS=OFS="," }
FNR==1 { for (i=1;i<=NF;i++) f[$i]=i; next }
{ print $(f["interesting"]) }

$ awk -f tst.awk file1 file2
gold
diamonds
platinum
sodium

创建名称 - &gt;值数组始终是适用的最佳方法。它使代码的每个部分都保持简单并与代码的其余部分分离,并且它可以帮助您进行其他操作,例如在输出结果时更改字段的顺序,例如:

$ cat tst.awk
BEGIN { FS=OFS="," }
FNR==1 { for (i=1;i<=NF;i++) f[$i]=i; next }
{ print $(f["junk"]), $(f["interesting"]), $(f["bar"]) }

$ awk -f tst.awk file1 file2
ramjet,gold,2
superfluous,diamonds,25
garbage,platinum,3
liverwurst,sodium,2.5