【问题标题】:Reassigning awk array during loop在循环期间重新分配 awk 数组
【发布时间】:2017-06-28 22:43:22
【问题描述】:

我的问题如下: 我有一个以某种方式格式化的文本文件:

12 apple 
78 orange 
12 prune
12 prune
78 berries
78 cake

我需要的是重新分配表单中的值:

12 apple, prune
78 orange, berries, cake

我使用 awk {sbj=$2","; a[$1]=a[$1]sbj}END{for (i in a) print i, a[i]} 做到了 但问题是现在我有了文件,格式为:

12 apple one
78 orange one

12 prune two
12 prune two
78 berries two
78 cake two

愿望输出是:

12 apple one
78 orange one

12 prune two
78 berries, cake two

我试图混合使用 awk 和 bash,但没有帮助。我现在可以做的唯一选择是将每个系列(“一”,“二”)存储到单独的文件中,并用上面的代码分别处理它们,然后将所有数组汇总到一个文件中。但这是一个笨重且笨拙的解决方案。有没有办法把它放在同一个文件中? 谢谢你的任何提示。

【问题讨论】:

  • 是我,太笨了,一开始就问不出问题。 ;)(第一个示例的输出也不完全正确,这让我很困惑)。当时忙着回答,但现在恢复了接近投票。
  • 第一个示例的输出有什么问题?在我看来是对的。
  • 如果你尝试给定的代码 sn-p 它将是 12 apple, prune prune 而不是 12 apple prune 这让我很困惑。

标签: arrays bash awk


【解决方案1】:
$ cat tst.awk
NF {
    if (!seen[$1,$2]++) {
        arr[$1] = ($1 in arr ? arr[$1] "," OFS : "") $2
        sfx[$1] = $3
    }
    next
}
{ prt() }
END { prt() }

function prt(   i) {
    for (i in arr) {
        print i, arr[i], sfx[i]
    }
    print ""
    delete sfx
    delete arr
    delete seen
}

$ awk -f tst.awk file
12 apple one
78 orange one

12 prune two
78 berries, cake two

请注意,以上内容将由in 运算符以随机顺序打印输出行 - 如果您想保留原始输入顺序,则在保存值时需要一个额外的步骤。然而,它会按照它们在输入中出现的顺序打印块和 $2 值。

【讨论】:

  • 报告的输出不完全诚实,它会打印并在末尾附加空行。 ;) 我喜欢 NF {} 部分,比 !NF{next} 更好,但我会保持原样。
  • 由于该站点不会在格式化代码/文本 sn-p 的末尾显示空白行,因此我们不知道 OP 中是否存在所需的输出。我选择认为是:-)。我确实有一个错误,虽然我为第二个块打印了两次“修剪”,但我现在已经修复了。
  • 艾德莫顿,谢谢你的回答。如果我可以问,您能否解释一下代码开头的“NF”符号和“!seen[$1,$2]++”语句的含义。因为我不明白它是如何工作的,如果我们执行“下一个”语句:因此,正如我从用户手册中了解到的那样,“下一个”语句应该创建一个数组,直到它到达文件的末尾,所以在输出应该是“ 12 apple, prune, prune \n 78 orange, berries, cake”?
  • NF 是包含当前行中字段数的 awk 变量。当该行为空时,NF 为零。所以NF { action }只有在当前行不为空的时候才会执行action。所以在上面的代码中,我们一直在非空行上填充数组,当我们遇到空行时,NF 为假,所以我们不执行操作(在我们的代码中包括next),所以我们移动到下一个打印然后清除数组的块上。阅读 Arnold Robbins 的《Effective Awk Programming, 4th Edition》来学习 awk。
  • 现在对我来说很清楚了。感谢您的回答和推荐的书!
【解决方案2】:

使用 GNU awk,您可以使用多维数组:

foo.awk:

!NF{next} # Skip empty lines
{a[$3][$1]=a[$3][$1]" "$2}
END{
    for(i in a){
        for(ii in a[i]){
            print ii" "a[i][ii]" "i
        }
        print ""
    }
}

像这样运行它:

gawk -f foo.awk input.file

我需要提一下,上述解决方案有一个缺点,除了它只适用于gawk:不能保证输出是有序的。那是因为for(i in a) 默认不保证任何顺序。 gawk 支持一个特殊的数组变量PROCINFO,可用于强制对数组进行单独排序:

foo.awk:

BEGIN {
    PROCINFO["sorted_in"] = "@ind_str_asc"
}
!NF{next} # Skip empty lines
{a[$3][$1]=a[$3][$1]" "$2}
END{
    for(i in a){
        for(ii in a[i]){
            print ii" "a[i][ii]" "i
        }
        print ""
    }
}

【讨论】:

  • 你应该提到这将产生随机/散列顺序的输出(例如,它可以在第一个块之前打印第二个块,以及每个块中的行以任何顺序)当 OP 尝试时在他的小输入集上,他可能会以与输入中出现的顺序相同的顺序获得输出,并认为这肯定会发生。
  • hek2mgl,谢谢你对多维数组的介绍,对我来说是新的。
  • 不客气。我添加了一些关于排序输出的句子。要获得稳定、可预测的解决方案,您应该遵循这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-28
  • 1970-01-01
  • 2014-02-16
  • 1970-01-01
  • 1970-01-01
  • 2012-02-22
  • 1970-01-01
相关资源
最近更新 更多