Regex lookahead для «не следует» в grep - PullRequest
89 голосов
/ 08 февраля 2012

Я пытаюсь выполнить grep для всех случаев Ui\., за которыми не следует Line или даже просто буква L

Как правильно написать регулярное выражение для нахождения всех экземпляров определенной строки, за которой НЕ следует другая строка?

Использование Lookaheads

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


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

Ответы [ 5 ]

126 голосов
/ 08 февраля 2012

Отрицательный взгляд, который вам нужен, требует более мощного инструмента, чем стандартный grep.Вам нужен grep с поддержкой PCRE.

Если у вас есть GNU grep, текущая версия поддерживает опции -P или --perl-regexp, и вы можете использовать необходимое регулярное выражение.

Если у вас нет (достаточно свежей версии) GNU grep, подумайте о том, чтобы получить ack.

34 голосов
/ 21 июня 2013

Ответ на часть вашей проблемы находится здесь, и ack будет вести себя так же: Ack и отрицательный прогноз, выдающий ошибки

Вы используете двойные кавычки для grep, что позволяетbash, чтобы «интерпретировать ! как команду расширения истории».

Вам нужно обернуть свой шаблон в ОДНОКВАТНЫЕ КВАРТИРЫ: grep 'Ui\.(?!L)' *

Однако, см. @ JonathanLeffler's answer для решения проблем с негативными взглядами в стандарте grep!

8 голосов
/ 01 ноября 2014

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

Для рассматриваемого регулярного выражения вы можете сделать что-то вроде

grep 'Ui\.' * | grep -v 'Ui\.L'
3 голосов
/ 06 января 2016

Если вам нужно использовать реализацию регулярных выражений, которая не поддерживает отрицательные подсказки, и вы не возражаете против сопоставления дополнительных символов *, то вы можете использовать классы отрицательных символов [^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] или $.

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

* Если ваша реализация поддерживает группы без захвата , тогда вы можете избежать захвата дополнительных символов.

0 голосов
/ 22 апреля 2017

Если ваш grep не поддерживает -P или --perl-regexp, и вы можете установить grep с поддержкой PCRE, например, «pcregrep», тогда для запуска регулярных выражений, совместимых с Perl, не потребуются никакие параметры командной строки, такие как GNU grep, просто запустите

pcregrep "Ui\.(?!Line)"

Вам не нужна другая вложенная группа для «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;};}"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...