【问题标题】:Recognising backslash in awk field separator识别 awk 字段分隔符中的反斜杠
【发布时间】:2020-07-28 20:43:42
【问题描述】:

输入是

AZE D11/879\x0Dabc\x0D\x0A\x1E!DEF F11/999

awk 脚本将字段分隔符设置为“\x0D”(我尝试过转义和不转义反斜杠。

awk 脚本是

BEGIN {FS="\\x0D"}
   {print NF}

它应该输出 3,因为字段分隔符出现了 2 次,但它输出 1 表示它未被识别。

【问题讨论】:

  • 嗯,经验法则是不断添加“\”直到它起作用。

标签: awk


【解决方案1】:

有两种方法可以在 awk 中提供正则表达式 - 静态正则表达式(也称为正则表达式文字),写为 /regexp/ 和动态正则表达式(又名计算正则表达式),写为 "regexp" 并在正则表达式上下文中使用。字段分隔符只是一个带有一些附加行为的正则表达式,所以我们只考虑一般的正则表达式来解释您的示例中发生了什么。

split() 函数将字段分隔符(用于我们目的的正则表达式)作为它的第三个参数,因此它提供了一个很好的测试平台:

使用静态正则表达式:

$ awk '{print split($0,a,/\x0D/)}' file
1

上面的\ 转义了x,它不是文字\。为此,您需要转义 \ 本身:

$ awk '{print split($0,a,/\\x0D/)}' file
3

如果我们使用动态正则表达式代替上述静态正则表达式会怎样?

$ awk '{print split($0,a,"\x0D")}' file
1
$ awk '{print split($0,a,"\\x0D")}' file
1
$ awk '{print split($0,a,"\\\x0D")}' file
' is not a known regexp operator FNR=1) warning: regexp escape sequence `\
1
$ awk '{print split($0,a,"\\\\x0D")}' file
3

上述行为是因为 awk 首先解析字符串以将其转换为正则表达式(使用一层转义字符),然后在 使用 将其作为正则表达式时再次解析它(使用第二层转义字符)。

不幸的是,当您指定 FS 时,没有选项可以将其指定为文字正则表达式,它总是使用字符串指定,因此是动态正则表达式,因此需要额外的转义层:

$ awk -v FS='\x0D' '{print NF}' file
1
$ awk -v FS='\\x0D' '{print NF}' file
1
$ awk -v FS='\\\x0D' '{print NF}' file
' is not a known regexp operatorence `\
1
$ awk -v FS='\\\\x0D' '{print NF}' file
3

现在 - 如果您在脚本的 shell 部分使用了错误类型的引号,即 " 而不是 ',该怎么办?然后你会引入更多的痛苦,因为现在你邀请 shell 在 awk 看到并解析它两次之前解析字符串:

$ awk -v FS="\\\\x0D" '{print NF}' file
1
$ awk -v FS="\\\\\x0D" '{print NF}' file
' is not a known regexp operatorence `\
1
$ awk -v FS="\\\\\\x0D" '{print NF}' file
' is not a known regexp operatorence `\
1
$ awk -v FS="\\\\\\\x0D" '{print NF}' file
3

这与在 awk 中使用双引号的情况不同,因为双引号都包含在单引号中,因此已经受到 shell 的保护:

$ awk 'BEGIN{FS="\\\\x0D"} {print NF}' file
3

所以 - 在 shell 中始终使用限制性最强的引号(' 而不是 " 而不是无),除非您有非常具体的理由不这样做,并且在使用正则表达式或字段分隔符时始终使用文字 /.../ 而不是动态"...",除非您有非常具体的理由不这样做。

上面奇怪的截断错误消息是由于我们提供的转义序列,工具试图打印的\rs,它们实际上都是warning: regexp escape sequence '\^M' is not a known regexp operator

【讨论】:

    【解决方案2】:

    由于\ 是转义字符,因此您需要两个反斜杠作为文字反斜杠:

    $ echo 'AZE D11/879\x0Dabc\x0D\x0A\x1E!DEF F11/999' |
        awk 'BEGIN{ FS="\\\\x0D" } { print NF }'
    3
    

    【讨论】:

      猜你喜欢
      • 2011-09-16
      • 2012-10-23
      • 1970-01-01
      • 2018-08-07
      • 2014-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-02
      相关资源
      最近更新 更多