Regex lookahead для "не супроводжується" в "grep"


103

Я намагаюся похвалитись за всі випадки Ui\.неслідування Lineчи навіть просто листаL

Який правильний спосіб написати регулярний вираз для пошуку всіх екземплярів певного рядка НЕ ​​за іншим рядком?

Використання місць пошуку

grep "Ui\.(?!L)" *
bash: !L: event not found


grep "Ui\.(?!(Line))" *
nothing

5
Який підвид регексу - PCRE, ERE, BRE, grep, ed, sed, perl, python, Java, C, ...?
— Джонатан Леффлер

4
В сторону "подія не знайдена" походить із використання розширення історії. Ви можете вимкнути розширення історії, якщо ви ніколи його не використовуєте, а іноді хочете мати можливість використовувати знак оклику у своїх інтерактивних командах. set +o histexpandв Bash або set +H, YMMV.
— трійчатка

12
У мене також було питання розширення історії. Я думаю, що я вирішив це просто, перейшовши на окремі лапки, тому оболонка не намагатиметься зв'язати аргумент.
— Coderer

@Coderer, що вирішив і мою проблему. Дякую.
— NHDaly

Відповіді:


151

Негативний пошук, який ви шукаєте, потребує більш потужного інструменту, ніж стандартний grep. Вам потрібна греп-програма з підтримкою PCRE.

Якщо у вас є GNU grep, поточна версія підтримує параметри -Pабо --perl-regexpви можете використати потрібний регулярний вираз.

Якщо у вас немає (досить новітньої версії) GNU grep, тоді подумайте про отримання ack.


37
Я впевнений, що проблема в цьому випадку полягає лише в тому, що в баші вам слід використовувати одинарні, а не подвійні лапки, тому це не трактується !як особливий символ.
— NHDaly

(див. нижче мою відповідь, що точно описує це.)
— NHDaly

4
Перевірена, правильна відповідь повинна поєднувати цю відповідь та коментар @ NHDaly. Наприклад, ця команда працює для мене: grep -P '^. * Містить ((?! But_not_this).) * $' * .Log. *> "D: \ temp \ result.out"
— wangf

3
Для тих , де -Pне підтримує TRY результат трубопроводу знову grep --invert-match, виключаючи: git log --diff-filter=D --summary | grep -E 'delete.*? src' | grep -E --invert-match 'xml'. Обов’язково підкресліть відповідь @Vinicius Ottoni.
— Данило Соколовський

@wangf Я використовую Bash під Cygwin, і коли я переходжу на окремі лапки, я все одно отримую помилку "подія не знайдена".
— SSilk

40

Відповідь на частину вашої проблеми є тут, і Ack поводитиметься так само: Ack & negative lookahead дає помилки

Ви використовуєте подвійні лапки для grep, що дозволяє bash "інтерпретувати !як команду розширення історії".

Вам потрібно загорнути свій візерунок в ЄДИННІ ЦИТИ: grep 'Ui\.(?!L)' *

Однак дивіться відповідь @ JonathanLeffler, щоб вирішити проблеми з негативними голосовими головами у стандарті grep!


Ви плутаєте функціональність розширення GNU grepз функціоналом стандарту grep, де стандартним grepє POSIX. Те, що ви говорите, також вірно - я запускаю Bash з відключеними варварствами C-shell (тому що, якби я хотів оболонку C, я використовував би її, але я не хочу її), тому !матеріал не впливає на мене - але щоб отримати негативні лукахеди, вам потрібні нестандартні grep.
— Джонатан Леффлер

1
@JonathanLeffler, дякую за роз’яснення; Я думаю, ви праві, що для вирішення всіх симптомів ОП потрібні обидві наші відповіді. Дякую.
— NHDaly

11

Ви, мабуть, не можете виконувати стандартні негативні шуми, використовуючи grep, але зазвичай ви повинні мати можливість отримати рівноцінне поведінку за допомогою "зворотного" перемикача "-v". Використовуючи це, ви можете побудувати регулярний вираз для доповнення того, що ви хочете відповідати, а потім передати його через 2 грепи.

Для регенезу, про який йдеться, ви можете зробити щось подібне

grep 'Ui\.' * | grep -v 'Ui\.L'

Це виключає б більше речей, більше випадків, якщо рядок містить Ui.Line та Ui без
— .Line

1
(Так, тому я не формулюю це строго. Це просто вирішує значну частину сценаріїв, які орієнтують людей на цю проблему. Більше нічого.)
— Карел Тучек

4

Якщо вам потрібно скористатися програмою регулярного вираження, яка не підтримує негативні підказки, і ви не заперечуєте проти відповідних додаткових символів *, ви можете використовувати заперечні класи символів[^L] , чергування| та кінець якоря рядка$ .

У вашому випадку grep 'Ui\.\([^L]\|$\)' *робить свою роботу.

  • Ui\. відповідає рядку, який вас цікавить

  • \([^L]\|$\)відповідає будь-якому окремому символу, окрім Lабо він відповідає кінці рядка: [^L]або $.

Якщо ви хочете виключити більше одного символу, то вам просто потрібно кинути на нього більше чергування та заперечення. Щоб знайти, aне слід bc:

grep 'a\(\([^b]\|$\)\|\(b\([^c]\|$\)\)\)' *

Який або ( aслідом за ним не, bабо за ним закінчується кінець рядка: aтоді [^b]або $), або ( aслідом за bяким або слідує ні, cабо слідує кінець рядка: aтоді b, потім [^c]або $.

Цей вид вираження стає досить громіздким і схильним до помилок навіть у короткому рядку. Ви можете написати щось, щоб генерувати вирази для вас, але, мабуть, було б простіше просто скористатися реалізацією регулярних виразів, яка підтримує негативні lookaheads.

* Якщо ваша реалізація підтримує групи, що не захоплюють, тоді ви можете уникнути захоплення зайвих символів.


1

Якщо ваша grep не підтримує -P або --perl-regexp, і ви можете встановити grep з підтримкою PCRE, наприклад, "pcregrep", то для прийняття регулярного сумісного з Perl не потрібні такі параметри командного рядка, як GNU grep вирази, ти просто біжиш

pcregrep "Ui\.(?!Line)"

Вам не потрібна інша вкладена група для "Рядок", як у вашому прикладі "Ui. (?! (Line))" - зовнішня група достатня, як я показав вище.

Дозвольте навести ще один приклад розгляду негативних тверджень: коли у вас є список рядків, повернутих ipset, кожен рядок містить кількість пакетів посередині рядка, і вам не потрібні рядки з нульовими пакетами, вам просто запустити:

ipset list | pcregrep "packets(?! 0 )"

Якщо вам подобаються регулярні вирази, сумісні з perl, і у вас perl, але у вас немає pcregrep або ваша grep не підтримує --perl-regexp, ви можете однорядкові сценарії perl, які працюють так само, як grep:

perl -e "while (<>) {if (/Ui\.(?!Lines)/){print;};}"

Perl сприймає stdin так само, як і grep, наприклад

ipset list | perl -e "while (<>) {if (/packets(?! 0 )/){print;};}"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.