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