【问题标题】:Using pipe and shell command in awk script在 awk 脚本中使用管道和 shell 命令
【发布时间】:2019-12-23 17:05:18
【问题描述】:

我正在编写一个需要生成需要排序的输出的 awk 脚本。

我能够在 awk 数组中获得所需的未排序输出。我尝试使用以下代码对数组进行排序并且它可以工作,但我不知道为什么以及它是否是预期的行为。

问题输入示例:

Ram,London,200
Alex,London,500
David,Birmingham,300
Axel,Mumbai,150
John,Seoul,450
Jen,Tokyo,600
Sarah,Tokyo,630

预期的输出应该是:

Birmingham,300
London,700
Mumbai,150
Seoul,450
Tokyo,1230

需要以下脚本来显示城市名称以及第三个字段中存在的整数的相应累积总数。

BEGIN{
    FS = ","
    OFS = ","
}
{
    if($2 in arr){
        arr[$2]+=$3;
    }else{
        arr[$2]=$3;
    }
}
END{

    for(i in arr){
        print i,arr[i] | "sort"
    }
}

以下代码有问题:

for(i in arr){
    print i,arr[i] | "sort"
}

print 的输出通过管道传递给 sort,这是一个 bash 命令。 那么,这个输出是如何从 awk 传输到 bash 的呢?

这是预期的行为还是仅仅是副作用?

有更好的 awk 方法吗?已经尝试过 asort 和 asorti,但它们存在 gawk 而不是 awk。

PS:我正在尝试专门为该任务编写一个 .awk 文件,而不使用 bash 命令。请提出相同的建议。

【问题讨论】:

  • 如果你不想使用像 sort 这样的外部程序或 gawk 特定的东西(我自己会使用 PROCINFO["sorted_in"]="@ind_str_asc" 来使用它来获得排序输出),你必须编写你的awk 中自己的排序例程。如果您通过四处搜索找到现有的,也不会感到惊讶。
  • @Shawn 这是合法使用还是只是副作用?
  • 管道 awk print 输出到另一个程序?是的,这是有效的。

标签: awk


【解决方案1】:

您能否尝试在您的代码中将您的sort 更改为sort -t',' -k1。由于您的分隔符是逗号,因此您需要通知 sort 您的分隔符与空格不同。默认情况下,排序将分隔符作为逗号。

您也可以删除 if, else 块 ftom 您的主块,您只能使用 arr[$2]+=$3。除了我上面提到的排序更改之外,保留其余代码

我在手机上,所以无法粘贴所有代码,但这里的解释应该对你有所帮助。

【讨论】:

  • 我的问题是关于在 awk 脚本中使用外部程序(如 sort),而不是关于程序的正确性。不过感谢您的意见。
  • 恕我直言,因为我没有看到您的代码使用排序有任何问题,所以我没有对此发表评论。
【解决方案2】:

我的建议是将awk 的输出通过管道传输到sort,而不是尝试担心在END 规则内传输输出。虽然 GNU awk 提供 asorti() 以允许对数组的内容进行排序,但在这种情况下,因为它只是您想要排序的输出,所以在您的 awk 脚本完成后,一个指向 sort 的管道就是您所需要的,例如

$ awk -F, -v OFS=, '{a[$2]+=$3}END{for(i in a)print i, a[i]}' file | sort
Birmingham,300
London,700
Mumbai,150
Seoul,450
Tokyo,1230

由于它是输出的单个管道,因此管道所需的子外壳不会产生 每次迭代 开销。

如果你想完全避免管道,如果你有 bash,你可以简单地使用 process-substitution 重定向,例如

$ sort < <(awk -F, -v OFS=, '{a[$2]+=$3}END{for(i in a)print i, a[i]}' file)
(same result)

如果你有 GNU awk,那么asorti() 将按索引对a 进行排序,你可以将排序后的数组放入一个新数组b,然后在END 规则内输出排序后的结果,例如

$ awk -F, -v OFS=, '{a[$2]+=$3}END{asorti(a,b);for(i in b)print b[i], a[b[i]]}' file
Birmingham,300
London,700
Mumbai,150
Seoul,450
Tokyo,1230

【讨论】:

    【解决方案3】:

    按顺序解决您的具体问题:

    那么,这个输出如何从 awk 传输到 bash?

    派生进程的管道。

    这是预期的行为还是仅仅是副作用?

    预期

    有更好的 awk 方法吗?已经尝试过 asort 和 asorti,但它们存在 gawk 而不是 awk。

    是的,将整个 awk 命令的输出通过管道传送到 sort。

    PS:我正在尝试专门为该任务编写一个 .awk 文件,而不使用 bash 命令。请提出相同的建议。

    查看https://web.archive.org/web/20150928141114/http://awk.info/?Sorting 了解一些在 awk 中常见的排序算法的实现。另见https://rosettacode.org/wiki/Category:Sorting_Algorithms。


    关于你的 cmets 中的问题:

    由于在 END 规则的循环中生成了一个进程以进行排序,我很困惑这是否会在一行上调用排序函数,然后生成的进程将在那里死掉,并且一个新的排序进程将是在循环的下一次迭代中产生

    在您的 awk 脚本终止或您调用 close("sort") 之前,生成的进程不会终止。

    【讨论】:

    • 由于在 END 规则中的循环内生成了一个进程以进行排序,我很困惑这是否会在一行上调用排序函数并且生成的进程将在那里死去,并且一个新的排序过程将在循环的下一次迭代中产生
    • 好的。非常感谢您的准确回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多