AWK 多字符分隔符

AWK 多字符分隔符

我在 Ubuntu 上使用 Bash,我的问题如下:

我有一个大文本文件,带有标题和分隔符#|#

我正在尝试使用 AWK 来获取有关此文件的一些信息。现在我想使用以下表达式计算第 2 列组按第 1 列值分组的总和:

awk 'BEGIN { FS="\\#\\|\\#" }{arr[$1]+=$2} END {for (i in arr) {print i,arr[i]}}' myfile.txt

我得到的输出有两个问题:

  • 首先,如果假设第 1 列采用两个唯一值 value1 和 value2,则 AWK 形成的不是 2 个而是 3 个组:value1、value2 以及 name_column1。

    好像它不明白文件的第一行是标题......

  • 第二个问题是我的输出是:

    value1        0
    value2        0
    name_column1  0
    

    所以我们知道输出的最后一行是意外的(如前所述),所以让我们关注前两行。在这里,两个总和都为空,但我知道其中至少一个应该严格大于 0,因为命令

    awk 'BEGIN { FS="\\#\\|\\#" }{sum1+=2;}END{print sum1;}' myfile.txt
    

    给我251597850

因此,要么是我的最后一个命令(常规求和)有问题,要么是前一个命令(求和+分组依据)有问题。

有人知道如何解决这个问题吗?

编辑:我的文件文本看起来像这样:

Column1#|#Column2#|#Column3

0300#|#0.00#|#0000

其中 0300 是value1前面提到的(它不是数字而是类别)。

编辑2:

awk 'BEGIN { FS="\\#\\|\\#" }{sum1+=2;}END{print sum1;}' myfile.txt

给我 2*(文件中的行数),这显然不是我想要的,所以命令应该是:

awk 'BEGIN { FS="\\#\\|\\#" }{sum1+=$2;}END{print sum1;}' myfile.txt

编辑3:

事实证明,由于分隔符的原因,我的两个命令都是错误的。因此,分组依据的正确命令是:

awk 'BEGIN { FS="#[|]#" } FNR>1 {arr[$1]+=$2} END { for (i in arr) print i,arr[i] }' file.txt

答案1

简单的回答是,在这种情况下 FS 变量是 RE(正则表达式或模式)。因此,如果任何实际数据字符在 RE 上下文中是“特殊”的,则需要在 RE 中对其进行转义,以确保将其视为自身,而不是运算符。

在这种情况下,罪魁祸首是|,它是交替运算符。它两侧的项目都是替代 RE,其中任何一个都将被视为匹配项。例如,字段分隔符a|u|o|i|e将在每个元音处拆分字段。

因此, RE#|#有点多余:它指定#为字段分隔符两次,并忽略重复。

解决方法是逃避|.我的首选方法是将 转换|为括号表达式(字符类),[|]从而将 降级|以表示自身。

或者,可以通过 转义字符\,因此分隔符可以写为#\\|#

我说逃避是\——为什么我写了两遍?这是另一个奇怪的规则(也是反斜杠经常导致 awk 模式出现问题的原因)。

有两种编写 awk RE 的方法:作为模式,如/myRE/,或作为字符串,如"myRE"

/myRE/表单(默认情况下)作为布尔值工作,可以单独用作pattern { action }awk 源模型中的模式,或者在{ if (/myRE/) ...}.在这种情况下,它与整行匹配,因为没有语法指示表明它还应该应用什么。它还可以与更具体的目标(如字段$6 ~ /myRE/或变量)进行匹配myVar ~ /myRE/。在这种形式中,字符由单个 转义\

然而,当 RE 被写为字符串时,awk 不知道它稍后可能会作为 RE 被调用。已解析两次:在原始源代码中第一次进行通常的字符串转义(例如\t制表符、\n换行符和\\反斜杠):然后当它与~运算符或在match()orsplit()函数中一起使用时再次进行转义。

声明 FS 被视为字符串,因此任何反斜杠都必须加倍。无论您是在命令行上使用-F或声明 FS,还是像 那样声明 FS,都是如此。-v FS=BEGIN { FS = "myRE" }

我提到了“简要回答”,这样的事情几乎总是错误的。有一个例外,还有那个例外的例外。

编写一个单字符正则表达式是很困难的,因为特殊运算符需要一些东西来操作。因此,FS 的任何单字符值都被视为字面上的本身。您可以编写'-F|'or-v 'FS=|'BEGIN { FS = "|" }并让字段由管道符号分割。

单字符规则的例外是由单个空格组成的 FS(这是默认设置)。这只是将行中的每个单词变成一个字段。作为 awk,简单是一个比较术语:

(1) 分隔符是“空白”,定义为 ASCII 空格、水平制表符和换行符的任意连续混合序列。 (如果替代记录分隔符有效,您只会看到换行符。)

(2) 整个行中的前导和尾随空白不是字段分隔符。 (如果任何其他 FS 位于行的开头或结尾,则在其之前或之后分别有一个隐含的额外空白字段。)

我的首选参考是GNU/awk 在线手册

尽管这个答案本身长得离谱且复杂,但该手册在第 3 节——正则表达式中用了大约 600 行,在第 4.5 节——指定如何分隔字段中又用了 250 行。

相关内容