С помощью grep, как я могу сопоставить шаблон, а инвертировать - другой шаблон?


11

С помощью 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

Ответы:


10

Если у вас есть 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}
'

8

Вы, кажется, используете 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


@ 1_CR ... Сэр ... это потрясающе ..: P что-то улыбаетсяack
Рахул Патил

@RahulPatil. :-), да GNU grep это хорошо.
iruvar

Это не совсем то, что я хочу. Я хочу, чтобы это работало, «игнорируй меня» перед или после «иглы».
Flimm

@RahulPatil, спасибо, я исправил это в последней версии
iruvar

Очень полезный. Особенно в случае grep с контекстом, где вы хотите исключить близко совпадающие линии, но без определенной части шаблона. Близко к первоначальному вопросу, но не совсем то же самое.
gaoithe

2

Я бы предложил вместо этого использовать 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

ignore meТогда есть несколько в файле, awk не работает
Rahul Patil

@RahulPatil: не могли бы вы перефразировать или добавить больше деталей к вашему вопросу? Я не понимаю, что вы спрашиваете.
Тор

@Thos протестировать ваш пример с этим входным файлом paste.ubuntu.com/6252860
Рахул Патил

@RahulPatil: Теперь я понимаю, что вы имеете в виду. Вариант 1 предполагает, что --\nмежду каждой сопоставленной группой находится разделитель, которого нет, если группы соседствуют друг с другом. То, как должны обрабатываться смежные группы, зависит от конкретной задачи, поэтому это не обязательно неправильно. Вариант 2 не зависит от разделителя и не затрагивается.
Тор
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.