为什么空匹配正则表达式在 sed 和 grep 中的工作方式不同?

为什么空匹配正则表达式在 sed 和 grep 中的工作方式不同?

例如,使用 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

相关内容