使用 -o 时 GNU/macOS grep 输出差异的混淆

使用 -o 时 GNU/macOS grep 输出差异的混淆

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

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

但这里所有的话:

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

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

$ 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

GNUgrep将为[a-z]+(或[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

答案1

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

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

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

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

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

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

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


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

               if (r == 0) {
                        c = 1;
                        if (oflag && pmatch.rm_so != pmatch.rm_eo)
                                goto print;
                        break;
                }
        }
        if (oflag)
                return c;
print:

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

相关内容