有两种方法可以在 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