使用 -o 时对 GNU/macOS grep 输出差异的困惑

ch4*_*h2o 6 grep regular-expression

为什么grepmacOS上的 BSD在这里只产生第一个词:

$ echo "once upon a time" | grep -o "[a-z]*"
once
Run Code Online (Sandbox Code Playgroud)

但这里所有的话:

$ echo "once upon a time" | grep -o "[a-z][a-z]*"
once
upon
a
time
Run Code Online (Sandbox Code Playgroud)

或者,使用扩展的正则表达式:

$ echo "once upon a time" | grep -E -o "[a-z]*"
once

$ echo "once upon a time" | grep -E -o "[a-z]+"
once
upon
a
time
Run Code Online (Sandbox Code Playgroud)

GNUgrep将为[a-z]+(or [a-z][a-z]*) 和生成相同的输出[a-z]*

$ echo "once upon a time" | ggrep -E -o "[a-z]*"
once
upon
a
time

$ echo "once upon a time" | ggrep -E -o "[a-z]+"
once
upon
a
time
Run Code Online (Sandbox Code Playgroud)

Kus*_*nda 3

收集评论部分的想法,这似乎归结为不同的grep实现决定如何处理空匹配以及[a-z]*空字符串上的表达式匹配。

\n\n

-o选项不是由 POSIX 定义的,因此实现方式如何处理它留给开发人员。

\n\n

GNUgrep显然会丢弃空匹配,例如once使用 时后面的空字符串的匹配[a-z]*,并继续从下一个字符开始处理输入。

\n\n

BSDgrep似乎正在打空火柴,并决定,无论出于何种原因,这就足够了,并就此停止。

\n\n

St\xc3\xa9phane 提到,ast-open的版本实际上在aftergrep的空匹配处进入无限循环,并且不会超过字符串中的该点。[a-z]*once

\n\n

OpenBSDgrep似乎与 macOS 和 FreeBSD 不同grep,因为添加-w标志(要求匹配由单词边界分隔)使得[a-z]*单独返回每个单词。

\n\n

ilkkachu 观察到,-o在某种意义上允许匹配空字符串的模式是令人困惑的(或者可能至少是模棱两可的)。是否应该打印所有空匹配项?事实上,给定字符串中的每个单词后面都有无限多个这样的匹配。

\n\n
\n\n

OpenBSD 源代码grep(表现出与grepmacOS 上相同的行为)包含 ( src/usr.bin/grep/util.c):

\n\n
               if (r == 0) {\n                        c = 1;\n                        if (oflag && pmatch.rm_so != pmatch.rm_eo)\n                                goto print;\n                        break;\n                }\n        }\n        if (oflag)\n                return c;\nprint:\n
Run Code Online (Sandbox Code Playgroud)\n\n

这基本上是说,如果模式匹配 ( r == 0) 并且我们使用-o( oflag),并且如果匹配开始偏移量与匹配结束偏移量相同( ,即空匹配),则不会打印pmatch.rm_so == pmatch.rm_eo匹配结果该特定输入行上的匹配结束( “找到匹配”)。return cc == 1

\n