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