Grep 正则表达式的结果不符合预期?

Grep 正则表达式的结果不符合预期?

使用 FreeBSD 11.1:

#!/bin/sh

if printf 'abcde.fgh' | grep -iEq '^[^][$^*_-]'; then
    echo "test 1 success"
else
    echo "test 1 fail"
fi

echo

if printf 'abcde.fgh' | grep -iEq '^[^][.$^*_-]'; then
    echo "test 2 success"
else
    echo "test 2 fail"
fi

输出:

test 1 success

grep: Unmatched [ or [^
test 2 fail

但据我所知,这些应该会给出相同的结果。它们都包含对第一个字符(仅第一个字符)的条件,即它不是指定的非字母字符列表中的一个字符。正则表达式的细分:

  • ^= 字符串的开头
  • [^...]= 如果这些字符均不匹配
  • 在列表中,]必须是第一个字符,^不能是第一个字符,并且 - 必须是最后一个字符。因此][.^$_-是有效的文字字符列表,并且字符串不能与其中任何一个匹配。
  • 为了避免混淆,请注意这意味着列表中的][是文字"]"和字符,"["不是关闭并重新打开 2 个列表。

这两个表达式之间的唯一区别是,"."但它在列表中,因此应将其视为not literal .并且第一个字符确实与文字不匹配"."

我遗漏了什么?可能是一些非常明显和简单的东西?

答案1

您遗漏了一些其他语法规则。在括号扩展中,除了普通范围之外,还有几种以 开头的多字符表达式[。(请参阅正则表达式(7)手册Linux或者FreeBSD“除了这些以及使用 '[' 的一些组合(见下一段)”。)这些是:

  • 整理元素:[..]
  • 等价类:[==]
  • 角色类别:[::]

(您可能已经看到或使用过这样的表达式[[:digit:]]——它们实际上是一个字符类[:digit:],恰好是[…]括号扩展的唯一元素。)

因此,在您的情况下,由于 恰好.紧跟在 之后[,因此它们被识别为排序元素的开始分隔符。 GNU grep 3.1 有正确的错误消息:

$ printf 'abcde.fgh' | grep -iEq '^[^][.$^*_-]'
grep: Unmatched [, [^, [:, [., or [=

如果没有地方可以移动它们,可以使用相同的表达式来逃避这种情况,例如使用[...][=.=]来包含一个常规点,或者类似地匹配一个破折号。[=-=]

相关内容