120

我正在尝试查找所有Ui\.未跟随Line甚至只是字母的实例L

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

使用前瞻

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


grep "Ui\.(?!(Line))" *
nothing
4

6 回答 6

167

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

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

如果您没有(足够新的版本) GNU grep,那么请考虑获取ack.

于 2012-02-08T18:08:18.793 回答
44

部分问题的答案在这里,并且 ack 的行为方式相同: Ack &negative lookaheadgiving errors

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

您需要将您的模式包装在单引号中: grep 'Ui\.(?!L)' *

但是,请参阅@JonathanLeffler 的回答来解决标准中的负前瞻问题grep

于 2013-06-20T21:04:55.583 回答
12

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

对于有问题的正则表达式,您可能会执行类似的操作

grep 'Ui\.' * | grep -v 'Ui\.L'
于 2014-11-01T09:43:09.290 回答
6

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

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

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

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

如果您想排除的不仅仅是一个字符,那么您只需要对其进行更多的交替和否定。查找a后面没有的bc

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

哪个是 (a后跟 notb或后跟行尾:athen[^b]$) 或 (a后跟bwhich 后跟 notc或后跟行尾:athen b, then[^c]$

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

*如果您的实现支持非捕获组,那么您可以避免捕获额外的字符。

于 2016-01-06T03:50:14.440 回答
2

至少对于在“Ui”之后不想要“L”字符的情况。你真的不需要PCRE。

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

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

于 2021-06-09T14:15:45.387 回答
1

如果您的 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 一样接受标准输入,例如

ipset list | perl -e "while (<>) {if (/packets(?! 0 )/){print;};}"
于 2017-04-22T06:26:22.753 回答