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