![使用 grep,如何匹配一个模式并反转匹配另一个模式?](https://linux22.com/image/51287/%E4%BD%BF%E7%94%A8%20grep%EF%BC%8C%E5%A6%82%E4%BD%95%E5%8C%B9%E9%85%8D%E4%B8%80%E4%B8%AA%E6%A8%A1%E5%BC%8F%E5%B9%B6%E5%8F%8D%E8%BD%AC%E5%8C%B9%E9%85%8D%E5%8F%A6%E4%B8%80%E4%B8%AA%E6%A8%A1%E5%BC%8F%EF%BC%9F.png)
使用grep
,我想选择与某个模式匹配且与另一个模式不匹配的所有行。我希望能够使用一次调用,grep
以便我可以使用--after-context
选项(或--before-context
、 或--context
)。
-v
在这里不可行,因为它否定了我传递给grep
使用该-e
选项的所有模式。
例子
我想寻找匹配的行needle
,忽略匹配的行ignore me
,并带有一行以下上下文。
这是我的输入文件:
one needle ignore me
two
three
four needle
five
我想要的输出是:
four needle
five
正如您所看到的,这个简单的解决方案不起作用:
$ cat file | grep --after-context=1 needle | grep -v 'ignore me'
two
---
four needle
five
答案1
如果你有 GNU grep,你可以使用Perl正则表达式,其中有一个否定结构。
grep -A1 -P '^(?!.*ignore me).*needle'
如果你没有 GNU grep,你可以在 awk 中模拟其前后上下文选项。
awk -v after=3 -v before=2 '
/needle/ && !/ignore me/ {
for (i in h) {
print h[i];
delete h[i];
}
until = NR + after;
}
{
if (NR <= until) print $0; else h[NR] = $0;
delete h[NR-before];
}
END {exit !until}
'
答案2
您似乎正在使用 GNUgrep。使用 GNU grep,您可以传入--perl-regex
标志来激活 PCRE,然后提供否定的先行断言,如下例
grep --after-context=1 \
--perl-regex '^(?:(?!ignore me).)*needle(?:(?!ignore me).)*$' file.txt
four needle
five
这里要注意的主要事情是(?:(?!STRING).)*
is to STRING
as [^CHAR]*
is toCHAR
答案3
我建议使用 awk 来代替,因为它可以更好地处理多行 IO。任何一个1)将结果通过管道传输到 GNU awk--\n
作为记录分隔符,或者2)在 awk 中进行所有匹配。
选项1
<file grep -A1 needle | awk '!/ignore me/' RS='--\n' ORS='--\n'
输出:
four needle
five
--
请注意,此选项在整个记录中搜索ignore me
、设置FS=1
并匹配$1
以仅与第一行进行比较。
选项2
<file awk 'a-- > 0; $0 ~ re1 && $0 !~ re2 { print $0; a=after }' re1=needle re2='ignore me' after=1