使用 grep,如何匹配一个模式并反转匹配另一个模式?

使用 grep,如何匹配一个模式并反转匹配另一个模式?

使用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

您似乎正在使用 GNU。使用 GNU grep,您可以传入--perl-regex标志来激活 PCRE,然后提供否定的先行断言,如下例

grep --after-context=1 \
--perl-regex '^(?:(?!ignore me).)*needle(?:(?!ignore me).)*$' file.txt
four needle
five

这里要注意的主要事情是(?:(?!STRING).)*is to STRINGas [^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

相关内容