在 grep 中寻找“ not follow by”的正则表达式前瞻

我试图抓取的所有实例的 Ui\.后面没有 Line,甚至只是字母 L

写一个正则表达式来查找一个特定字符串的所有实例(不后跟另一个字符串)的正确方法是什么?

使用前瞻

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




grep "Ui\.(?!(Line))" *
nothing
77389 次浏览

负面的前瞻,也就是您所追求的,需要一个比标准 grep更强大的工具。您需要启用 PCRE 的 grep。

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

如果您没有(足够新的版本) GNUgrep,那么考虑获得 ack

你的部分问题的答案就在这里,阿克也会有同样的表现: 返回和否定前瞻给出错误

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

你需要用单引号包装你的图案: grep 'Ui\.(?!L)' *

但是,请参阅 @ Jonathan Leffler 的回答以解决标准 grep中负面前瞻的问题!

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

对于所讨论的正则表达式,您可以执行以下操作

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

如果您需要使用不支持负向前看且不介意匹配额外字符 * 的正则表达式实现,那么可以使用 否定字符类 [^L]交替 |绳端锚 $

在你的情况下 grep 'Ui\.\([^L]\|$\)' *做的工作。

  • Ui\.匹配您感兴趣的字符串

  • \([^L]\|$\)匹配除 L以外的任何单个字符,或者匹配行尾: [^L]$

如果您想排除多个字符,那么您只需要对其进行更多的交替和否定处理。如果发现 a后面没有跟着 bc:

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

也就是(a后面跟着不是 b,或者跟着行尾: a后面跟着 [^b]或者 $)或者(a后面跟着 b,后面跟着不是 c,或者跟着行尾: a后面跟着 b,后面跟着 b0或者 $

这种表达式即使使用很短的字符串也会变得相当笨拙和容易出错。您可以编写一些东西来为您生成表达式,但是只使用支持负前瞻的正则表达式实现可能更容易。

* 如果您的实现支持 没有捕获的群体,那么您可以避免捕获额外的字符。

如果 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;};}"

至少对于不想在“ Ui”后面加一个“ L”字符的情况来说,你不需要 PCRE。

    grep -E 'Ui\.($|[^L])' *

在这里,我已经确保匹配的特殊情况下的“ Ui。”在行的结尾。