Как с помощью 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).)*есть STRINGкак [^CHAR]*естьCHAR

решение3

Я бы предложил использовать вместо этого awk, так как он лучше обрабатывает многострочный ввод-вывод. Либо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

Связанный контент