awk:致命:设置多个字段分隔符时无效的正则表达式

awk: fatal: Invalid regular expression when setting multiple field separators

我试图用 awk 解决 。问题包含一个字符串 XXXXXX[YYYYY--ZZZZZ 并且 OP 希望在文本中的唯一 [-- 字符串之间打印文本。

如果只有一个 - 我会说使用 [-[] 作为 field separator (FS)。这是将 FS 设置为 -[:

$ echo "XXXXXXX[YYYYY-ZZZZ" | awk -F[-[] '{print }'
YYYYY

棘手的一点是[作为一个字符class也有特殊的含义,所以为了使它被正确解释为可能的FS之一,它不能写在第一个位置。好吧,这是通过说 [-[] 来完成的。所以我们完成匹配 -[.

但是,在这种情况下,它不是一个而是两个连字符:我想说 --[。我不能说 [--[] 因为连字符也有定义范围的含义。

我能做的就是像这样使用-F"one pattern|another pattern"

$ echo "XXXXXXXaaYYYYYbbZZZZ" | awk -F"aa|bb" '{print }'
YYYYY

因此,如果我尝试将其与 --[ 一起使用,我将无法获得正确的结果:

$ echo "XXXXXXX[YYYYY--ZZZZ" | awk -F"--|[" '{print }'
awk: fatal: Invalid regular expression: /--|[/

事实上,甚至没有 [ 作为其中一项:

$ echo "XXXXXXX[YYYYYbbZZZZ" | awk -F"bb|[" '{print }'
awk: fatal: Invalid regular expression: /bb|[/

$ echo "XXXXXXX[YYYYYbbZZZZ" | awk -F"bb|\[" '{print }'
awk: warning: escape sequence `\[' treated as plain `['
awk: fatal: Invalid regular expression: /bb|[/

$ echo "XXXXXXX[YYYYYbbZZZZ" | awk -F"(bb|\[)" '{print }'
awk: warning: escape sequence `\[' treated as plain `['
awk: fatal: Unmatched [ or [^: /(bb|[)/

你看,我尝试过转义 [,用括号括起来,但没有任何效果。

那么:如何将字段分隔符设置为 --[?有可能吗?

您需要使用双反斜杠来转义双引号字符串中的正则表达式元字符,否则它将被视为正则表达式元字符(如果您使用单反斜杠)被视为转义序列。

$ echo 'XXXXXXX[YYYYYbbZZZZ' | awk -v FS="bb|\[" '{print }'
YYYYY

这与 GNU Awk 3.1.7

echo "XXXXXXX[YYYYY--ZZZZ" | awk -F"--|[[]" '{print }'    
echo "XXXXXXX[YYYYYbbZZZZ" | awk -F"bb|[[]" '{print }'

恕我直言,如果我们从查看 split() 命令使用的正则表达式开始,就可以最好地解释这一点,因为它明确显示了使用文字与动态正则表达式将字符串拆分为字段时发生的情况,然后我们可以将其与字段分隔符相关联。

这使用文字正则表达式(由 /s 分隔):

$ echo "XXXXXXX[YYYYY--ZZZZ" | awk '{split([=10=],f,/\[|--/); print f[2]}'
YYYYY

因此需要对 [ 进行转义,因此它是字面意思,因为 [ 是正则表达式元字符。

这些使用动态正则表达式(一个存储为字符串):

$ echo "XXXXXXX[YYYYY--ZZZZ" | awk '{split([=11=],f,"\[|--"); print f[2]}'
YYYYY

$ echo "XXXXXXX[YYYYY--ZZZZ" | awk 'BEGIN{re="\[|--"} {split([=11=],f,re); print f[2]}'
YYYYY

$ echo "XXXXXXX[YYYYY--ZZZZ" | awk -v re='\[|--' '{split([=11=],f,re); print f[2]}'
YYYYY

因此需要 [ 转义 2 次,因为 awk 必须将包含正则表达式的字符串(最后两个示例中名为 re 的变量)转换为正则表达式(使用在它用作 split() 调用中的分隔符之前(用完第二个反斜杠)。

这个:

$ echo "XXXXXXX[YYYYY--ZZZZ" | awk -v re="\\[|--" '{split([=12=],f,re); print f[2]}'
YYYYY

将变量内容暴露给 shell 进行评估,因此需要 [ 转义 3 次,因为 shell 首先解析字符串以尝试扩展 shell 变量等(用完一个反斜杠),然后 awk 必须将包含正则表达式的字符串转换为正则表达式(用完第二个反斜杠),然后再用作 split() 调用中的分隔符(使用第三个反斜杠)。

字段分隔符只是一个正则表达式,存储为名为 FS 的变量(如上面的 re),具有一些额外的语义,因此以上所有内容都适用于它,因此:

$ echo "XXXXXXX[YYYYY--ZZZZ" | awk -F '\[|--' '{print }'
YYYYY

$ echo "XXXXXXX[YYYYY--ZZZZ" | awk -F "\\[|--" '{print }'
YYYYY

请注意,我们可以使用方括号表达式而不是将其转义以按字面意思处理 [

$ echo "XXXXXXX[YYYYY--ZZZZ" | awk '{split([=14=],f,/[[]|--/); print f[2]}'
YYYYY

然后我们不必担心在添加解析层时转义转义:

$ echo "XXXXXXX[YYYYY--ZZZZ" | awk -F "[[]|--" '{print }'
YYYYY

$ echo "XXXXXXX[YYYYY--ZZZZ" | awk -F '[[]|--' '{print }'
YYYYY