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.