grep для «термин» и исключить «другой термин»

28

Я пытаюсь построить поиск grep, который ищет термин, но исключает строки, имеющие второй термин. Я хотел использовать несколько -e "pattern"вариантов, но это не сработало.

Вот пример команды, которую я пробовал, и сгенерированного сообщения об ошибке.

grep -i -E "search term" -ev "exclude term"
grep: exclude term: No such file or directory

Мне кажется, что это -vотносится ко всем условиям поиска / шаблонам. Как это работает, но затем не включает search termв результаты.

grep -i -E "search term" -ve "exclude term"
nelaaro
источник
Есть ли какой-либо другой вариант исключения, так как иногда нам приходится вставлять строки вокруг слова и, если мы исключаем их в следующей операции, используя '|' , оно просто удаляет это слово, но не удаляет блок для этого слова
Ученик

Ответы:

40

Чтобы и выражения с grep вам нужно два вызова:

grep -Ei "search term" | grep -Eiv "exclude term"

Если искомые термины не являются регулярными выражениями, используйте фиксированное сопоставление строк ( -F), которое быстрее:

grep -F "search term" | grep -Fv "exclude term"
Тор
источник
18

Если не вызывать grep дважды, я могу придумать только один способ сделать это. Он включает в себя Perl-совместимые регулярные выражения (PCRE) и некоторые довольно хакерские утверждения .

Для поиска foo, исключая совпадения, содержащие строку , вы можете использовать:

grep -P '(?=^((?!bar).)*$)foo'

Вот как это работает:

  • (?!bar)соответствует чему-либо, что не является баром, не потребляя символы из строки. Затем .потребляет один символ.

  • ^((?!bar).)*повторяет вышеизложенное от начала строки ( ^) до ее конца ( $). Он потерпит неудачу, если barвстретится в любой данный момент, так (?!bar)как не будет совпадать.

  • (?=^((?!bar).)*$) удостоверяется, что строка соответствует предыдущему шаблону, не потребляя символы из строки.

  • fooищет foo как обычно.

Я нашел этот хак в регулярном выражении, чтобы соответствовать строке, не содержащей слова? , В ответе Барта Киерса вы можете найти гораздо более подробное объяснение того, как работает негативное прогнозирование.

Деннис
источник
Хороший хак. Этот прием работает и в Java, кстати.
Раман
12

Если вы хотите сделать это за один проход, вы можете использовать awk вместо grep.

Формат:

echo "some text" | awk '/pattern to match/ && !/pattern to exclude/'

Примеры:

  • echo "hello there" | awk '/hello/ && !/there/'

Ничего не возвращает

  • echo "hello thre" | awk '/hello/ && !/there/'

Возвращает: привет три

  • echo "hllo there" | awk '/hello/ && !/there/'

Ничего не возвращает

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

Примеры:

  • echo "hello thre" | awk '(/hello/ || /hi/) && !/there/'

Возвращает: привет три

  • echo "hi thre" | awk '(/hello/ || /hi/) && !/there/'

Возвращает: привет три

  • echo "hello there" | awk '(/hello/ || /hi/) && !/there/'

Ничего не возвращает

  • echo "hi there" | awk '(/hello/ || /hi/) && !/there/'

Ничего не возвращает

Филип Риз
источник
1
Это сработало для меня, но я потерял цвета = P
Леопольдо Санчик
1
Цвета с какого выхода? Если вы пытаетесь сохранить цвета с помощью ls, используйте аргумент «--color = always» при каждом анализе выходных данных (или вы всегда будете терять цвета при синтаксическом анализе текста). Пример: ls --color=always | awk '/hello/ && !/goodbye/'
Филипп Риз
Спасибо за ответ @Philip! Я пробовал это раньше, но безуспешно. Я предполагаю, что, поскольку у шаблона есть цветной текст, он не совпадает позже, и я должен включить некоторый цветовой код в шаблон. В любом случае, ваш самый быстрый способ, который я нашел grep -Rв нескольких файлах кода с помощью командной строки Ubuntu.
Леопольдо Санчик
1

Из моих экспериментов не имеет особого значения, если вы передадите свои исключающие термины через grepили sed. Sed имеет некоторые другие полезные функции замены текста, которые я часто использую, чтобы лучше фильтровать выход файлов журнала. Поэтому я собираюсь использовать sed, так как объединяю довольно много фильтров в sed.

wc /var/log/tomcat/tomcat.2013-01-14.log.1 
  1851725

 / usr / bin / time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | sed -e "/ логин ОК / d" -e "/ логин истек / d" | Туалет
24.05пользователь 0.15система 0: 25.27 прошло 95% ЦП (0avgtext + 0avgdata 3504maxresident) k
0 входов + 0 выходов (0 основных + 246 минут)
   5614 91168 1186298

 / usr / bin / time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | sed -e "/ логин ОК / d" -e "/ логин истек / d" | Туалет
23.50user 0.16system 0: 24.48 истекло 96% CPU (0avgtext + 0avgdata 3504maxresident) k
0 входов + 0 выходов (0 основных + 246 минут)
   5614 91168 1186298

 / usr / bin / time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | grep -v -e "логин ОК" -e "логин истек" | Туалет
23.08пользователь 0.14система 0: 23.55 истекло 98% ЦП (0avgtext + 0avgdata 3504maxresident) k
0 входов + 0 выходов (0 основных + 246 минут)
   5614 91168 1186298

 / usr / bin / time grep -i -E "(loginmanager)" /var/log/tomcat/tomcat.2013-01-14.log.1 | grep -v -e "логин ОК" -e "логин истек" | Туалет
23.50пользователь 0.15система 0: 25.27 прошло 93% ЦП (0avgtext + 0avgdata 3488maxresident) k
0 входов + 0 выходов (0 основных + 245 минут)
   5614 91168 1186298

nelaaro
источник
3
Попробуйте сравнить время выполнения grep -Fвместо grep -Eи не используйте, -iесли вам это не нужно.
Тор
1
Но тогда вы не приводите примеры использования sed;)
Бенджамин R