Regex lookahead для 'не сопровождается' в grep

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

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

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

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


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

Ответ 1

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

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

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

Ответ 2

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

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

Вам нужно обернуть свой шаблон в SINGLE-QUOTES: grep 'Ui\.(?!L)' *

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

Ответ 3

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

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

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

Ответ 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] или $.

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

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

Ответ 5

Я думаю, что эта ссылка может помочь вам сначала понять, как работает регулярное выражение, а во-вторых, как создать ваше регулярное выражение: http://www.regular-expressions.info/tutorialcnt.html

Ответ 6

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

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;};}"