З грепом, як я можу зіставити візерунок та інвертувати відповідати іншому шаблону?


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, а потім подати негативне твердження lookahead, наприклад нижче

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, оскільки він краще обробляє багаторядковий 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

Чи є тоді декілька ignore meфайлів, awk не працює
Rahul Patil

@RahulPatil: Ви можете перефразувати або додати детальніше запитання? Я не розумію, про що ви питаєте.
Тор

@Тестуйте свій приклад за допомогою цього вхідного файлу paste.ubuntu.com/6252860
Rahul Patil

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