如何使用 gawk 多次传递?

How to use multiple passes with gawk?

我正在尝试使用 CYGWIN 中的 GAWK 来处理 csv 文件。第 1 遍找到最大值,第 2 遍打印匹配最大值的记录。我正在使用 .awk 文件作为输入。当我使用手册中的文本时,它在两次通过时都匹配。我可以使用 IF 形式作为解决方法,但这迫使我在每个模式匹配中使用 IF,这有点痛苦。知道我做错了什么吗?

这是我的 .awk 文件:

pass == 1
{
    print "pass1 is", pass;  
}    

pass == 2
{
if(pass == 2)
    print "pass2 is", pass;  
}    

这是我的输出(输入文件只是“hello”):

hello
pass1 is 1
pass1 is 2
hello
pass2 is 2

这是我的命令行:

gawk -F , -f test.awk pass=1 x.txt pass=2 x.txt

如有任何帮助,我将不胜感激。

(g)awk 解决方案可能如下所示:

awk 'FNR == NR{print "1st pass"; next}
     {print "second pass"}' x.txt x.txt

(如有必要,请将 awk 替换为 gawk。)
比方说,你想在文件 x.txt 的第一列中搜索最大值,然后在第一列中打印所有具有该值的行,你的程序可能看起来像这样(感谢 Ed Morton 一些提示,见评论):

awk -F"," 'FNR==NR {max = ( (FNR==1) || ( > max) ?  : max ); next}
           ==max'  x.txt x.txt

x.txt的输出:

6,5
2,6
5,7
6,9

6,5
6,9

这是如何工作的?变量 NR 随着每条记录不断增加,而 FNR 在读取新文件时重置为 1。因此,FNR==NR 仅适用于处理的第一个文件。

所以... F.Knorr 准确简洁地回答了您的问题,他应该得到一个大大的绿色勾号。 NR==FNR 正是您要找的秘方。

但这里有一个不同的方法,以防万一多遍的事情被证明是有问题的。 (也许您正在从慢速驱动器、USB 记忆棒、网络、DAT 磁带等读取文件)

awk -F, '>m{delete l;n=0;m=}m=={l[++n]=[=10=]}END{for(i=1;i<=n;i++)print l[i]}' inputfile

或者,为了便于阅读,将空格分开:

BEGIN {
  FS=","
}

 > max {
  delete list           # empty the array
  n=0                   # reset the array counter
  max=                # set a new max
}

max== {
  list[++n]=[=11=]          # record the line in our array
}

END {
  for(i=1;i<=n;i++) {   # print the array in order of found lines.
    print list[i]
  }
}

使用与 F.Knorr 测试相同的输入数据,我得到相同的结果。

这里的想法是一次通过文件。我们将与最大值匹配的每一行记录在一个数组中,如果我们遇到超过最大值的值,我们将清除数组并重新开始收集行。

这种方法在 CPU 和内存(取决于数据集的大小)上较重,但作为单次传递,它可能在 IO 上较轻。

这里的问题是换行符对 awk 很重要。

# This does what I should have done: 
pass==1 {print "pass1 is", pass;} 
pass==2 {if (pass==2) print "pass2 is", pass;}

# This is the code in my question:
# When pass == 1, do nothing
pass==1 
# On every condition, do this
    {print "pass1 is", pass;} 
# When pass == 2, do nothing
pass==2 
# On every condition, do this
    {if (pass==2) print "pass2 is", pass;}

使用 pass==1,pass==2 不那么优雅,但它有效。