使用AWK在块中求和(在模式更改时重新启动总和)

Modified on: Sat, 30 Jun 2018 09:55:30 +0800

我有一个这样的文件:

A 100
A 200
A 300 #sum=600
B 400
B 500 #sum=900
A 600
A 700
A 800 #sum=2100

我希望输出为:

A 600
B 900
A 2100
C sum_of_C
D sum_of_D

我可以使用forsedgrepawk

但是因为我正在学习awk,所以我想写一个awk脚本。到目前为止,我有:

if (${NR {print $1}} == ${NR-1 {print $1}}) 
  sum+=$2
  print $0"\t"sum
else
  sum=$2
  print $0"\t"sum

awk -f awkscript file未成功。解决方案是什么?

作者:polym,Ooker

最佳答案

我不完全确定你的if试图在那里做什么。 NR是记录数;使用NF获取字段数,如果这是你的目标。你不能把{}块放在这样的东西中。

我认为你的目标是将这一行中的一个字段的值与前一行中的字段进行比较,当我们到达一个新的“数据组”时打印出总和。如果是这样的话,这个脚本会做你想要的,我认为这几乎与你的目标相同:

{ if (last && $1 != last) { print last, sum sum = 0 } sum = sum + $2 last = $1 } END { print last, sum }

我们创建一个新变量last来保存前一行的第一个字段($1)的值。我们将使用它来跟踪我们正在查看的组。

  • 对于每一行(因为我们在顶层有{ ... }),我们首先测试是否设置了a)last(因为我们没有想要在第一行打印任何东西),和b)第一个字段的值不同于last。如果是,我们打印出last的值,一个空格(因为,),以及我们计算的sum。 (如果你想要一个标签,请像你一样在引号中使用"\t"
  • 打印后,我们将sum重置为零。
  • 无论哪种方式,我们都会将第二个字段($2)的值添加到sum
  • 对于每一行,我们将第一个字段(我们的组)保存到last中,这样我们就可以在下一行用它进行比较。
  • 最后,我们也希望打印出最后一组。为此,我们使用END { ... }块。当我们用完数据时,它会在程序结束时运行。我们打印出的金额和我们正在使用的小组就像以前一样。

如果我跑:

{
    if (last && $1 != last) {
        print last, sum
        sum = 0
    }
    sum = sum + $2
    last = $1
}
END {
    print last, sum
}

使用您的数据文件,我得到了这个输出:

awk -f sum.awk < data

根据需要。


有更简单的方法可以做到这一点,无论是在awk还是其他方面。特别是,我们可以用以下内容替换上面的主体:

A 600
B 900
A 2100

这里我们使用awk的条件块语法而不是显式的if test:这个程序的行为与上面的相同,但它更加惯用。在这个例子中并没有太大的不同,但了解你是否正在学习awk是有用的。


如果您提供的文件示例实际上是这样的,使用#sum=行(或类似),您可以使用此脚本:

{ sum = sum + $2 if (NF == 3) { print $1, sum sum = 0 } }

对于每一行,这会将第二个字段的值添加到sum变量中。在具有三个字段(NF == 3)的行上,我们打印出总数,并将sum重置为零。


相关问答

添加新评论