grep -w 仅匹配行中模式的第一次出现

grep -w 仅匹配行中模式的第一次出现

我试图理解为什么grep -w(GNU 实现的版本 3.1)仅匹配一行中特定模式的第一次出现。

这是一个例子。我希望它会匹配n1,n2n3,但它只匹配第一个。

$ echo 'n1=1 n2=2 n3=3' | grep -ow "n[0-9]=*"
n1

或者,如果我告诉它仅匹配n2n3,它再次匹配第一个,并忽略n3

$ echo 'n1=1 n2=2 n3=3' | grep -ow "n[23]=*" 
n2

我在这里缺少什么?对于这种行为有任何解释吗,或者这是 grep 中的某种错误?

这个想法是匹配:

  1. n[0-9]前面和后面都是非单词字符。
  2. n[0-9]以 开头,后跟任意数量的=字符,以非单词字符结尾的子字符串。

例如,如果字符串是n1=1 n2=== n3=3 n4== n5,则预期结果应该是:

n1
n2===
n3
n4==
n5

澄清:我知道这个目标可以实现grep -ow -e 'n[0-9]' -e "n[0-9]=*",但这不是重点。该问题的目的是了解如何grep工作。

加法测试

如果我添加n<num>=到行中的不同位置(等号后没有后续单词字符),它也会匹配这些字符,但它会再次忽略n3=3.

$ echo 'n1=1 n2= n3=3 n4=' | grep -ow "n[0-9]=*"
n1
n2=
n4=

我发现的最后一件事是,如果我添加将-P模式解释为 Perl 兼容的正则表达式,它似乎不会保留-w子字符串的描述“必须位于行尾或后跟非单词组成字符”,因为n1=即使后面跟着字符,它也会匹配1,这是一个单词组成字符(“字母、数字和下划线”)。

$ echo 'n1=1 n2= n3=3 n4=' | grep -owP "n[0-9]=*"
n1=
n2
n3=
n4

所以看起来grep -wP搜索字边界在子字符串的末尾而不是非单词组成字符。它似乎相当于:

$ echo 'n1=1 n2= n3=3 n4=' | grep -o "\bn[0-9]=*\b"
n1=
n2
n3=
n4

答案1

接得好。这似乎确实是一个错误grep (用 3.4 和 3.7 测试GNU grep):

grep -ow "n[0-9]=*"
grep -Eow "n[0-9]=*"

仅返回第一个匹配项(或者可能只匹配第一个匹配项),
而...

grep -Pow "n[0-9]=*"

...按预期返回所有匹配项。

要报告 的错误GNU grep,请检查这里


然而,我无法证实你的观察-P[...]似乎没有保留-w描述,对我来说(GNU grep 3.4 和 3.7),该命令按预期输出:

$ echo 'n1=1 n2= n3=3 n4=' | grep -owP "n[0-9]=*"
n1
n2=
n3
n4=

答案2

例如,如果字符串是n1=1 n2=== n3=3 n4== n5,则预期结果应该是:

n1
n2===
n3
n4==
n5

澄清:我知道可以通过以下方式实现该目标grep -ow -e 'n[0-9]' -e "n[0-9]=*"

对此也不太确定:

u$ grep --version |head -1
grep (GNU grep) 2.27
u$ printf '%s\n' 'n1=1 n2=== n3=3 n4== n5' | grep -ow -e 'n[0-9]' -e "n[0-9]=*"
n1
n2===
n3
n4==
n5

a$ grep --version |head -1
grep (GNU grep) 3.4
a$ printf '%s\n' 'n1=1 n2=== n3=3 n4== n5' | grep -ow -e 'n[0-9]' -e "n[0-9]=*"
n1
n2===
n4==
n5

n3请注意较新的 grep 是如何缺失的。这是在Ubuntu上,结果与3.7相同。

而对于 Busybox,答案又不同了:

$ printf '%s\n' 'n1=1 n2=== n3=3 n4== n5' | busybox grep -ow -e 'n[0-9]' -e "n[0-9]=*"
n1
n2
n3
n4
n5

我的 Mac 上的 BSD grep 也会打印n1, n2... 等,但每个打印两次。无论出于何种原因。

喜欢斯特凡在评论中说,用于-w此目的似乎不便携。


这个想法是匹配:

  • n[0-9]前面和后面都是非单词字符。

  • n[0-9]以 开头,后跟任意数量的=字符,以非单词字符结尾的子字符串。

在我看来,您想要在 Perl 中实现类似的功能(按此顺序,以便=具有优先权):

/ n[0-9]=*(?=\W) | \bn[0-9]\b /x

例如

$ printf '%s\n' 'n1=1 n2=== n3=3 n4== n5' |
    perl -lne 'print $& while / n[0-9]=*(?=\W) | \bn[0-9]\b /xg'
n1
n2===
n3
n4==
n5

但我不确定这是否是您想要的。如果它n2===x代替n2===,则输出为n2==,因为最后一个=用于满足“以非单词字符结尾”子句。 (或者更确切地说,“后面是”,否则 for 的匹配也n1=1应该是n1=。那就是n1,后跟零=s,后跟非单词字符=。)

我认为你可以使用所有格量词=*+来避免返回任何符号=,所以:

$ printf '%s\n' 'n1=1 n2===X n3=3 n4== n5' |
    perl -lne 'print $& while / n[0-9]=*+(?=\W) | \bn[0-9]\b /xg'
n1
n2
n3
n4==
n5

无论如何,我想知道通过简单的逻辑是否可以更好地实现您想要做的事情,即仅在空格上拆分字符串,在符号上拆分子字符串=,然后查看各个值。

相关内容