Grep – How to Match a Pattern and Invert Match Another Pattern

grep

With grep, I want to select all lines that match a pattern, and that don't match another pattern. I want to be able to use a single invocation of grep so that I can use the --after-context option (or --before-context, or --context).

-v is not viable here, as it negates all patterns I pass to grep using the -e option.

Example

I want to look for lines matching needle, ignoring lines matching ignore me, with one line of following context.

Here's my input file:

one needle ignore me
two
three
four needle
five

The output I want is:

four needle
five

As you can see, this naive solution doesn't work:

$ cat file | grep --after-context=1 needle | grep -v 'ignore me'
two
---
four needle
five

Best Answer

If you have GNU grep, you can use Perl regular expressions, which have a negation construct.

grep -A1 -P '^(?!.*ignore me).*needle'

If you don't have GNU grep, you can emulate its before/after context options in 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}
'
Related Question