GNU awk 手册的第 3.4节如下

要在括号表达式中包含字符“\”、“]”、“-”或“^”之一,请在其前面放置“\”。例如:
     [d\]]
匹配“d”或“]”。此外,如果您将“]”放在开头“[”之后,则结尾括号将被视为要匹配的字符之一。

括号表达式中的 ‘\’ 的处理与其他 awk 实现兼容,并且也是 POSIX 所要求的。

另一方面,部分没有将 列为具有特殊含义。以下是使用 GNU awk(版本 5.3.1)和 GNU grep(版本 3.11)进行的一些实验,这些实验揭示了对括号表达式中的 的冲突处理:\]\

$ echo d | awk '/[d\]]/'
d
$ echo d | grep -E '[d\]]'
$ echo ']' | awk '/[d\]]/'
]
$ echo ']' | grep -E '[d\]]'

问题是: GNU awk 文档声称GNU awk 中括号表达式
的处理是 POSIX 规定的,这是错误的吗?还是我忽略了什么?换句话说,GNU awk 是否违反了 POSIX 规范?\

0


最佳答案
3

\允许 awk 将括号表达式解释为转义字符的POSIX参考位于中的正则表达式下的表中(重点是我的,特别注意表格的最后两行):

…这些转义序列应在括号表达式的内部和外部被识别

转义序列 描述 意义
\" <反斜杠> <引号> 在词汇标记 STRING 中,字符。否则未定义。
\/ <反斜杠> <斜杠> 在词汇标记 ERE 中,<slash> 字符。否则未定义。
\ddd 一个 <backslash> 字符,后跟一个、两个或三个八进制数字的最长序列 (01234567)。… 用一位、两位或三位八进制整数表示的字符…
\., \[, \(,\*, \+, \?, \{, \|, \^, \$ <backslash> 字符后跟在 ERE 中具有特殊含义的字符…而不是 <backslash>。 在词汇标记 ERE 中,当不在括号表达式内时,序列应表示其自身。否则未定义。
\\ 两个 <反斜杠> 字符。 在词汇标记 ERE 中,序列应代表其自身……
\c <反斜杠> 字符后跟本表或 XBD 5. 文件格式符号 ( ) 中的表中未描述的任何字符'\\', '\a', '\b', '\f', '\n', '\r', '\t', '\v' 不明确的

这意味着在符合 POSIX 标准的awk括号表达式之内或之外的\\被要求表示文字,\而 的含义\c,其中c是表中未列出的任何字符(例如]),POSIX 未定义,因此gawk可以随意处理它,例如,可以[d\]]表示“d]”。

因此,不,在处理 时gawk并没有违反(它取代了用于描述 awk 行为的\) ,因为它是\按照 所要求的方式[\\]和允许的方式(因为其含义未定义)进行处理的[d\]]

18

  • 那么,gawk 文档中声称匹配“d”或“]”显然是错误的[d\]],不是吗?POSIX 没有强制要求这样做;它可能匹配,但不必匹配。这种构造不能保证可移植。


    – 

  • POSIX 指出这[d\]]是未定义的行为,因此任何 awk 变体(例如 BSD 或 GNU)都可以将其定义为实现者想要的任何含义。gawk 文档指出这[d\]]意味着d] gawk 中的– 这在 gawk 文档中并不是错误,就像定义函数的作用或部分中gensub()的值一样,因为这就是 gawk 的含义$0END


    – 


  • 1
    关于“该构造不能保证可移植。” – gawk 文档并未声称它在其他 awk 中的含义与在 gawk 中的含义相同,只是在 gawk 中的含义相同。一般来说,如果您希望代码可移植,则应避免在代码中使用任何行为未由 POSIX 定义的构造(不幸的是,现在的最新规范中也有一些已定义但几乎没有 awk 实现的构造),无论它在您使用的工具变体中是如何定义的。


    – 

  • 2
    @M.NejatAydin 我与 gawk 提供商 Arnold 进行了交谈 – 他将在 gawk 手册中添加更多关于此问题的解释性文字,并且我将针对 POSIX 开具一张票据,以改进标准中对此领域的描述。


    – 


  • 另请注意,gawk --posix '/[d\>]/'会发出警告,而gawk --posix '/[d\]]/'不会。虽然\>是 GNU 扩展,但两个括号表达式对于 POSIX 都是无效的。


    – 


规定

括号表达式要么是匹配列表表达式,要么是非匹配列表表达式。它由一个或多个表达式组成:普通字符、排序元素、排序符号、等价类、字符类或范围表达式。如果右方括号 ( ‘]’ ) 出现在列表中的第一个位置(如果有,则在初始脱字符 ( ‘^’ 之后),则它将失去其特殊含义并在括号表达式中表示自身。否则,它将终止括号表达式,除非它出现在排序符号中(例如 “[.].]” )或者是排序符号、等价类或字符类的结尾。特殊字符 ‘.’、’*’、'[‘ 和 ‘\’(分别为句点、星号、左方括号和反斜杠)将在括号表达式中失去其特殊含义。

字符序列“[.”、“[=”和“[:”(左方括号后跟句点、等号或冒号)”在括号表达式中应为特殊字符,用于分隔排序符号、等价类表达式和字符类表达式。这些符号后应跟有效表达式和匹配的终止序列“.]”、“=]”或“:]”,如以下各项所述(…)

所以

如果将 ‘]’ 放在开头 ‘[‘ 之后,则结尾括号将被视为要匹配的字符之一

符合上述规定,但

[d\]]

据我理解,这并不意味着 匹配“d”或“]”,因为第一个]是终止的,因为它既不是第一个字符,也不是排序符号、等价类或字符类的元素。

0

通常,创建字符类比担心需要多少个反斜杠(以及每层封装会“吃掉”多少个反斜杠)更容易。因此,如果您想要一个ERE可以在grep -Eawk用于捕获 "d"(仅小写字母)和/或"]"(右方括号)的统一,那么请执行

awk '/[]d]/'
或者
awk '$0 ~ "[]d]"'


grep -E '[]d]'

只有^(仅适用于累积字符类,并且仅当它是字符类中最左侧的项目时)和\需要在字符类中进行特殊的反斜杠处理。其他特殊项目(如]和)-可以通过在类中进行战略性放置来避免使用反斜杠。

取反^(即除插入符号之外的任何语言环境有效字符)—> [^^]

我通常将-\\放在 char 类的最右边,]最左边。如果您需要-and \:= [-\\]。如果您需要所有这三个 { ], -, \},也许[]-\\]。通过\\尽可能放置在最右边,它们被误认为是除反斜杠本身之外的任何转义序列的可能性为零。

如果可能的话,通过字符范围捕获这几个字符,并完全避免使用反斜杠 – 例如,ASCII通过明确列出字符范围来捕获所有标点符号,可以这样做

awk '/[!-/:-@[-\140{-~]/'gawk / mawk

awk '/[!-\/:-@[-\140{-~]/'nawk


awk '$0 ~ "[!-/:-@[-\140{-~]"'

(我使用它 \140 是因为在我的代码中我不喜欢在任何地方使用物理反引号,但是使用它的字符串版本是安全\140\\140

此规则的唯一例外是,如果您需要]限制字符范围的上限。在这种情况下,选择您的毒药:

awk '/[[-\]]/'或者awk '/[][\\]/'