正则表达式在grep中预示``不跟随''


103

我正在尝试对所有Ui\.不跟随Line字母甚至字母的情况进行grepL

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

提前使用

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


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

5
正则表达式的哪些亚种-PCRE,ERE,BRE,grep,ed,sed,perl,python,Java,C,...?
乔纳森·勒夫勒

4
顺便说一句,“未找到事件”来自使用历史记录扩展。如果您从未使用过历史扩展,则可能要关闭它,有时还希望能够在交互式命令中使用感叹号。set +o histexpand在Bash或set +HYMMV中。
Tripleee'2

12
我还遇到了历史扩展问题。我我只是通过切换为单引号来解决了该问题,因此Shell不会尝试修改该参数。
Coderer

@Coderer也解决了我的问题。谢谢。
NHDaly

Answers:


151

负向超前,这就是您所追求的,它需要比standard更为强大的工具grep。您需要启用了PCRE的grep。

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

如果您没有GNU(最新版本)grep,请考虑获取ack


37
我很确定在这种情况下的问题是,在bash中,您应该使用单引号而不是双引号,这样它就不会被!视为特殊字符。
NHDaly 2013年

(请参阅下文,我的回答将准确描述这一点。)
NHDaly 2014年

4
经过验证的正确答案应结合此答案和@NHDaly的评论。例如,此命令对我有用
wangf

3
对于-P不支持的那些,请再次尝试将结果管道输送到grep --invert-match,例如:git log --diff-filter=D --summary | grep -E 'delete.*? src' | grep -E --invert-match 'xml'。确保支持@Vinicius Ottoni的答案。
Daniel Sokolowski

@wangf我在Cygwin下使用Bash,当我更改为单引号时,仍然出现错误“未找到事件”。
SSilk

39

部分问题的答案在这里,而ack的行为方式相同: Ack和否定超前提示错误

您正在对grep使用双引号,这使bash可以“解释!为历史记录扩展命令”。

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

但是,请参阅@JonathanLeffler的答案,以解决标准中的负先行问题grep


您将GNU的扩展功能grep与standard的功能混为一谈grep,其中的标准grep是POSIX。您所说的也是正确的-我在禁用C-shell野蛮行为的情况下运行Bash(因为如果我想要一个C shell,我会使用一个,但是我不想要一个),所以这些!内容不会影响我-但是要获得负面的反感,您需要使用非标准grep
Jonathan Leffler

1
@JonathanLeffler,感谢您的澄清;我认为您是对的,它需要我们提供两个答案才能解决OP的所有症状。谢谢。
NHDaly 2014年

10

您可能无法使用grep执行标准的否定先行,但通常您应该能够使用“反向”开关'-v'获得同等的行为。使用它,您可以为要匹配的内容构造一个正则表达式,然后将其通过2个粗线传送。

对于正则表达式,您可能会做类似的事情

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

那将排除更多的东西,如果该行包含Ui.Line和不带.Line的Ui,则会有更多实例
nafg

1
(是的,这就是为什么我没有严格地表述它。这只是解决了导致人们转向此问题的大部分情况,仅此而已。)
Karel Tucek

4

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

在您的情况下,grep 'Ui\.\([^L]\|$\)' *就可以了。

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

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

如果要排除多个字符,则只需要对它进行更多的替换和否定即可。查找后a不跟bc

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

其是(a接着通过不b或随后行的末尾:a然后[^b]$)或(a随后b其或者随后不c或之后是该线的端部:a然后b,然后[^c]$

即使很短的字符串,这种表达也很笨拙并且容易出错。您可以编写一些内容来为您生成表达式,但是仅使用支持否定先行的正则表达式实现可能会更容易。

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


1

如果您的grep不支持-P或--perl-regexp,并且您可以安装启用PCRE的grep(例如“ pcregrep”),则它将不需要任何命令行选项(例如GNU grep)来接受与Perl兼容的常规表达式,您只需运行

pcregrep "Ui\.(?!Line)"

您不需要在示例“ Ui。(?!(Line))”中为“ 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;};}"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.