正则表达式预测grep中的“not follow by”

问题描述 投票:88回答:5

我试图grep所有Ui\.的实例,而不是Line,甚至只是字母L

编写正则表达式以查找特定字符串的所有实例而不是其他字符串的正确方法是什么?

使用前瞻

grep "Ui\.(?!L)" *
bash: !L: event not found


grep "Ui\.(?!(Line))" *
nothing
regex grep regex-lookarounds
5个回答
126
投票

负面的前瞻,你所追求的,需要比标准的grep更强大的工具。您需要一个支持PCRE的grep。

如果您有GNU grep,当前版本支持选项-P--perl-regexp,然后您可以使用您想要的正则表达式。

如果你没有(一个足够新版本的)GNU grep,那么考虑获得ack


33
投票

部分问题的答案就在这里,而ack的行为方式相同:Ack & negative lookahead giving errors

你正在使用grep的双引号,它允许bash“将!解释为历史扩展命令”。

你需要在SINGLE-QUOTES中包装你的模式:grep 'Ui\.(?!L)' *

但是,请参阅@JonathanLeffler's answer以解决标准grep中负向前瞻的问题!


8
投票

您可能无法使用grep执行标准的负向前瞻,但通常您应该能够使用“反向”开关'-v'获得等效行为。使用它你可以构造一个正则表达式,以补充你想要匹配的东西,然后通过2个greps管道它。

对于有问题的正则表达式,你可能会做类似的事情

grep 'Ui\.' * | grep -v 'Ui\.L'

3
投票

如果你需要使用不支持负前瞻的正则表达式实现并且你不介意匹配额外的字符*,那么你可以使用negated character classes [^L]alternation |end of string anchor $

在你的情况下,grep 'Ui\.\([^L]\|$\)' *完成这项工作。

  • Ui\.匹配您感兴趣的字符串
  • \([^L]\|$\)匹配除L之外的任何单个字符,或者它匹配行的末尾:[^L]$

如果你想要排除多于一个字符,那么你只需要对它进行更多的交替和否定。找到a后面没有bc

grep 'a\(\([^b]\|$\)\|\(b\([^c]\|$\)\)\)' *

哪个是(a后跟不是b或者后面是行尾:a然后[^b]$)或(a后跟b,其后不是c或者后面是行尾:a然后b ,然后[^c]$

这种表达式变得非常笨拙并且即使是短字符串也容易出错。您可以编写一些内容来为您生成表达式,但是使用支持负前瞻的正则表达式实现可能更容易。

*如果您的实现支持non-capturing groups,那么您可以避免捕获额外的字符。


0
投票

如果你的grep不支持-P或--perl-regexp,你可以安装支持PCRE的grep,例如: “pcregrep”,它不需要任何命令行选项,比如GNU grep来接受与Perl兼容的正则表达式,你只需运行

pcregrep "Ui\.(?!Line)"

你不需要为“Line”设置另一个嵌套组,如你的例子“Ui。(?!(Line))” - 外部组就足够了,就像我上面所示。

让我举一个看负面断言的例子:当你有行列表,由“ipset”返回时,每行显示行中间的数据包数,你不需要零数据包的行,你只需要跑:

ipset list | pcregrep "packets(?! 0 )"

如果您喜欢与Perl兼容的正则表达式并且有perl但没有pcregrep或者您的grep不支持--perl-regexp,那么您可以使用与grep相同的单行perl脚本:

perl -e "while (<>) {if (/Ui\.(?!Lines)/){print;};}"

Perl以与grep相同的方式接受stdin,例如

ipset list | perl -e "while (<>) {if (/packets(?! 0 )/){print;};}"
© www.soinside.com 2019 - 2024. All rights reserved.