例如,使用 Debian 或 Debian 派生系统上的工具,像 之类的正则表达式N*
可以匹配空字符串,可能会导致 sed 中的匹配:
$ echo 'Hello' | sed 's/N*/ xx&xx /g'
xxxx H xxxx e xxxx l xxxx l xxxx o xxxx
xxxx
这是每个字符串字符之前的空匹配(因此是字符串,中间没有字符)的正确结果xx&xx
(在 中 6 次Hello
。尾随换行符不算在内,它不匹配)。
并且,如果任何字符(或字符组)匹配,它将出现在xx
和之间xx
:
$ echo 'Hello' | sed 's/e*/ xx&xx /g'
xxxx H xxexx l xxxx l xxxx o xxxx
然而,grep 中的相同正则表达式会不是匹配空字符串:
$ echo 'Hello' | grep -o 'N*'
但会打印仅有的非空匹配:
$ echo 'Hello' | grep -o 'e*'
e
grep 中是否有额外的内部规则来避免empty
正则表达式匹配?
答案1
grep -o
记录grep --help
为
-o, --only-matching show only nonempty parts of lines that match
和在手册中作为
仅打印匹配行的匹配(非空)部分,每个此类部分位于单独的输出行上。
所以是的,有一个额外的规则grep -o
:只有匹配项非空时才会输出。
在 中echo 'Hello' | grep -o 'N*'
,正则表达式匹配(通过查看返回码可以看出,或者使用echo 'Hello' | grep 'N*'
),但因为匹配为空,所以没有输出任何内容。
答案2
0 长度字符串匹配的行为在代码中可以是特殊情况,也可以不是。例如sed
但不是perl
$ echo aabb | sed 's/a*/X/g'
XbXbX
$ echo aabb | gsed 's/a*/X/g'
XbXbX
$ echo aabb | perl -ple 's/a*/X/g'
XXbXbX
行为可能来自 vi 历史上的行为方式正如在深处所见ex/ex_subst.c
:
/*
* !!!
* It's possible to match 0-length strings -- for example, the
* command s;a*;X;, when matched against the string "aabb" will
* result in "XbXbX", i.e. the matches are "aa", the space
* between the b's and the space between the b's and the end of
* the string. There is a similar space between the beginning
* of the string and the a's. The rule that we use (because vi
* historically used it) is that any 0-length match, occurring
* immediately after a match, is ignored. Otherwise, the above
* example would have resulted in "XXbXbX". Another example is
* incorrectly using " *" to replace groups of spaces with one
* space.
(另一个问题可能是零宽度匹配永远匹配;我很确定有“只是移动到下一个字符......”代码来防止这种情况,可能会添加后有人的 CPU 有几次达到 100%,手掌碰到了额头。)
BSD 和 GNU 都在表达式ed
上失败了s/a*/X/g
,所以特殊行为可能是一个ex-vi
时代变化,逃逸到sed
?
$ echo aabb > foo
$ ed foo
5
s/a*/X/g
?
s/a*/X
Xbb
Q
答案3
$ echo 'Hello' | grep -o 'N*'
$ echo $?
0
它做匹配该输入行的空子字符串,如退出状态所示。 (使用不同的模式,如N
,我们在标准输出上仍然什么也得不到,但退出状态为1
, failure。)
-o
使它不打印空匹配,但这与正则表达式是否匹配任何输入行无关。 (是的,我们可以区分这一点;如果它打印了一个空字符串匹配,它会在每次匹配后输出一个换行符,因此提示符之前会有一个空行。或者 6,每个匹配一个。)
如果没有-o
,它会打印整个匹配行:
$ echo 'Hello' | grep 'N*' # same as grep '' empty pattern
Hello