regex grep中'not followed by'的正则表达式前瞻

7bsow1i6  于 2023-02-20  发布在  其他
关注(0)|答案(6)|浏览(123)

我尝试对后面不跟Line甚至只跟字母L的所有Ui\.示例执行grep
什么是正确的方式来编写一个正则表达式来查找一个特定字符串的所有示例没有跟随另一个字符串?
使用监视器

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

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

q3aa05251#

负前瞻(这正是您所追求的)需要比标准grep更强大的工具,您需要一个支持PCRE的grep。
如果您有GNU grep,当前版本支持选项-P--perl-regexp,然后您可以使用您想要的正则表达式。
如果您没有GNU grep(足够新的版本),那么可以考虑使用ack

scyqe7ek

scyqe7ek2#

这里给出了部分问题的答案,ack的行为也是如此:Ack & negative lookahead giving errors
您对grep使用了双引号,这允许bash“将!解释为history expand命令”。
你需要把你的模式用单引号括起来:grep 'Ui\.(?!L)' *
然而,请参见@JonathanLeffler的回答,以解决标准grep中的负面问题!

eeq64g8w

eeq64g8w3#

您可能无法使用grep执行标准的负查找,但通常您应该能够使用“inverse”开关'-v'获得等效的行为。使用该开关,您可以构建一个正则表达式,用于您想要匹配的内容的补数,然后通过2个grep对其进行管道传输。
对于所讨论的正则表达式,您可以执行以下操作

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

(Edit:这不如真正的前瞻强大,但通常可用于解决问题。)

h4cxqtbf

h4cxqtbf4#

如果您需要使用不支持负lookahead的正则表达式实现,并且不介意匹配额外的字符 *,那么可以使用negated character classes [^L]alternation |end of string anchor $
在您的例子中,grep 'Ui\.\([^L]\|$\)' *完成了这项工作。

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

如果你想排除不止一个字符,那么你只需要抛出更多的交替和否定。要找到后面没有bca
x1米11米1x
它可以是(a后跟not b或后跟行尾:a,然后[^b]$)或(a,然后bb之后或者不是c,或者之后是行尾:然后是b,然后是[^c]$
这种表达式非常笨拙,即使字符串很短也容易出错,你可以编写一些东西来生成表达式,但是使用支持负向观察的正则表达式实现可能会更容易。

ttcibm8c

ttcibm8c5#

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

pcregrep "Ui\.(?!Line)"

对于“Line”,不需要像示例“Ui.(?!(Line))”中那样使用另一个嵌套组--外部组就足够了,就像我上面展示的那样。
让我再给予你一个看否定Assert的例子:当你有一个由“ipset”返回的行列表,每一行显示了行中间的包数,并且你不需要没有包的行,你只需要运行:

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

如果你喜欢perl兼容的正则表达式,并且有perl但是没有pcregrep,或者你的grep不支持--perl-regexp,你可以使用一行perl脚本,其工作方式与grep相同:

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

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

ipset list | perl -e "while (<>) {if (/packets(?! 0 )/){print;};}"
iezvtpos

iezvtpos6#

至少对于不希望在“Ui”后面有“L”字符的情况,您实际上并不需要PCRE。

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

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

相关问题