Grep正则表达式不包含一个字符串

302
我正在将一系列正则表达式模式传递给grep,以便与syslog文件进行匹配检查。通常它们用于匹配IP地址和日志条目。
grep "1\.2\.3\.4.*Has exploded" syslog.log

这只是一个模式列表,像"1\.2\.3\.4.*已爆炸"这部分我正在循环中传递,所以我不能传递"-v",例如。
我正在尝试做与上述相反的事情,而不是匹配具有特定IP地址和错误的行,因此"!1.2.3.4.*已爆炸"将匹配除了1.2.3.4之外的任何其他syslog行,告诉我它已经爆炸了。我必须能够包含一个IP地址来进行不匹配。
我在Stack Overflow上看到了各种类似的帖子。然而,它们使用的正则表达式模式似乎无法与grep一起工作。对于grep,有什么有效的示例吗?
这发生在以下脚本中;
patterns[1]="1\.2\.3\.4.*Has exploded"
patterns[2]="5\.6\.7\.8.*Has died"
patterns[3]="\!9\.10\.11\.12.*Has exploded"

for i in {1..3}
do
  grep "${patterns[$i]}" logfile.log
done

你的意思是有时候你想匹配一个模式,但有时候又想匹配除了某个特定模式以外的所有内容吗?(这似乎是一个奇怪的要求,但无论如何)。如果是这样,为什么不迭代两个不同模式列表呢? - beerbajay
我对正则表达式不是很了解;我不想用“Has Exploded”进行grep,因为我不想知道每个日志设备的情况,所以我能否在一个语句中同时使用“Has Exploded”和!9.10.11.12进行grep? - jwbensley
如果你非得用一条语句完成,那么像Neil建议的那样使用负回顾后发就可以了。请看我在那里的评论。 - beerbajay
使用PCRE风格的正则表达式匹配和负向先行断言,如@Neil的答案所示: patterns[3]="\!9\.10\.11\.12.*Has exploded" 更改为 patterns[3]="(?<!9\.10\.11\.12).*Has exploded"grep "${patterns[$i]}" logfile.log 更改为 grep -P "${patterns[$i]}" logfile.log PCRE默认假定更多的元字符,因此其他匹配表达式中可能需要删除一些转义符。 - Codex24
4个回答

553

grep匹配,grep -v则是反向匹配。如果您需要“匹配A但不匹配B”,通常会使用管道:

grep "${PATT}" file | grep -v "${NOTPATT}"

3
确实可以使用"-v",而且您可以在循环中使用它。也许您需要更具体地说明您的限制,或者您对脚本如何工作存在误解。尝试发布一些代码。 - beerbajay
1
但是如果A由B组成呢?换句话说,如果我想匹配没有A和带有AB的行怎么办?管道符号无法解决这个问题。 - pawamoy
grep -v 对我来说不起作用:它仍然输出包含NOTPATT中单词的行。 - Ihor B.
@IhorB。如果您的模式有问题,那么可能是其他原因导致的;请尝试使用一些测试数据单独测试每个模式,以查看它是否给出了您期望的结果。也许您正在使用未转义的特殊字符,例如 . - beerbajay
@beerbajay, 我想获取包含String1但不包含String2的文件。我使用了以下命令--> grep -Hrn "String1" . | grep -v -Hrn "String2"但它仍然打印包含两个字符串的文件..我的命令有什么问题? - Bhavuk Mathur
显示剩余5条评论

21
(?<!1\.2\.3\.4).*Has exploded

你需要使用-P选项来运行这个命令,以便进行负向回顾(Perl正则表达式),所以命令是:
grep -P '(?<!1\.2\.3\.4).*Has exploded' test.log

试试这个。它使用负向回顾以忽略在前面有1.2.3.4的行。


3
我相当确定 grep 不支持顺序环视,除非你使用 GNU grep 并使用 --P 参数来让它使用 PCRE 引擎。 - Tim Pietzcker
дёҚпјҢgrepдёҚж”ҜжҢҒиҝҷз§Қзұ»еһӢзҡ„жӯЈеҲҷиЎЁиҫҫејҸпјӣ$grep -P (?<!1.2.3.4) test.log -bashпјҡ`('йҷ„иҝ‘жңүиҜӯжі•й”ҷиҜҜгҖӮ - jwbensley
如果正则表达式包含会被shell解释的字符,您需要对其进行引用。 - beerbajay
正确引用:grep -P '(?<!1\.2\.3\.4) Has exploded' test.log。请注意,回顾仅适用于与表达式匹配部分紧接着的字符,因此如果地址和消息之间有其他内容,例如 1.2.3.4 FOO Has exploded,则此方法将无法正常工作。 - beerbajay
@TimPietzcker,非常敏锐。我会把这个加到问题里。另外,请注意在否定回顾后面有一个.*,因为他的例子也有它,我想可能还有其他文本在中间。 - Neil

13
似乎没有人发布过所有答案的优秀组合,正则表达式 (-E) 和匹配反转 (-v)。
grep -Ev 'pattern1|pattern2|pattern3' file

值得注意的是,不需要使用lookaround,因此即使您的grep版本没有可用的-P选项,也可以使用此方法。

2
patterns[1]="1\.2\.3\.4.*Has exploded"
patterns[2]="5\.6\.7\.8.*Has died"
patterns[3]="\!9\.10\.11\.12.*Has exploded"

for i in {1..3}
  do
grep "${patterns[$i]}" logfile.log
done

应该和之前的一样。
egrep "(1\.2\.3\.4.*Has exploded|5\.6\.7\.8.*Has died)" logfile.log | egrep -v "9\.10\.11\.12.*Has exploded"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接