Как выполнить поиск для нескольких шаблонов с шаблоном, имеющим символ трубы?


623

Я хочу найти все строки в нескольких файлах, которые соответствуют одному из двух шаблонов. Я попытался найти шаблоны, которые я ищу, набрав

grep (foo|bar) *.txt

но оболочка интерпретирует это |как канал и жалуется, когда barне является исполняемым файлом.

Как я могу grep для нескольких шаблонов в одном наборе файлов?



grep 'word1 \ | word2 \ | word3' / path / to / file
lambodar

Ответы:


860

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

grep 'foo*' *.txt

Если вам нужна одиночная кавычка, вы можете написать ее как '\''(конец строкового литерала, буквальная кавычка, открытая строковый литерал).

grep 'foo*'\''bar' *.txt

Во-вторых, grep поддерживает два синтаксиса для шаблонов. Старый синтаксис по умолчанию ( базовые регулярные выражения ) не поддерживает |оператор alternation ( ), хотя некоторые версии имеют его как расширение, но пишутся с обратной косой чертой.

grep 'foo\|bar' *.txt

Переносимым способом является использование более нового синтаксиса, расширенных регулярных выражений . Вам нужно передать -Eопцию, grepчтобы выбрать его. В Linux вы также можете печатать egrepвместо grep -E(в других единицах вы можете сделать это псевдонимом).

grep -E 'foo|bar' *.txt

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

grep -e foo -e bar *.txt

18
Как примечание - когда шаблоны исправлены, вы должны действительно привыкнуть к fgrepили grep -F, для небольших моделей разница будет незначительной, но по мере того, как они станут длиннее, преимущества начнут проявляться ...
TC1

7
@ TC1 fgrep устарела в соответствии с man-страницей
ramn

18
@ TC1 То, grep -Fимеет ли реальный выигрыш в производительности, зависит от реализации grep: некоторые из них в любом случае применяют один и тот же алгоритм, так что -Fэто влияет только на время, затрачиваемое на анализ шаблона, а не на время поиска. -FНапример, GNU grep работает не быстрее (в нем также есть ошибка, которая делает grep -Fмедленнее в многобайтовых локалях - тот же шаблон констант на grepсамом деле значительно быстрее!). С другой стороны, BusyBox grep извлекает большую выгоду из -Fбольших файлов.
Жиль

4
Возможно, следует упомянуть, что для более сложных шаблонов, где чередование должно быть только для части регулярного выражения, его можно сгруппировать с помощью «\ (» и «\)» (экранирование для стандартных базовых регулярных выражений по умолчанию) ) (?).
Питер Мортенсен

4
Обратите внимание, что egrepпредшествует grep -E. Это не специфично для GNU (это, безусловно, не имеет ничего общего с Linux). На самом деле, вы все равно найдете системы, такие как Solaris, где по умолчанию grepвсе еще не поддерживается -E.
Стефан Шазелас

90
egrep "foo|bar" *.txt

или же

grep "foo\|bar" *.txt
grep -E "foo|bar" *.txt

выборочно цитируя справочную страницу gnu-grep:

   -E, --extended-regexp
          Interpret PATTERN as an extended regular expression (ERE, see below).  (-E is specified by POSIX.)

Matching Control
   -e PATTERN, --regexp=PATTERN
          Use PATTERN as the pattern.  This can be used to specify multiple search patterns, or to protect  a  pattern
          beginning with a hyphen (-).  (-e is specified by POSIX.)

(...)

   grep understands two different versions of regular expression syntax: basic and extended.”  In  GNU grep,  there
   is  no  difference  in  available  functionality  using  either  syntax.   In  other implementations, basic regular
   expressions are less powerful.  The following description applies to extended regular expressions; differences  for
   basic regular expressions are summarized afterwards.

В начале я не читал дальше, поэтому я не узнал тонких различий:

Basic vs Extended Regular Expressions
   In basic regular expressions the meta-characters ?, +, {, |, (, and ) lose their special meaning; instead  use  the
   backslashed versions \?, \+, \{, \|, \(, and \).

Я всегда использовал egrep и ненужные парены, потому что я учился на примерах. Теперь я узнал что-то новое. :)


22

Как сказал ТС1, -Fпохоже, пригодный вариант:

$> cat text
some text
foo
another text
bar
end of file

$> patterns="foo
bar" 

$> grep -F "${patterns}" text
foo
bar

1
@poige Я не знал о параметре $ 'foo \ nbar', не уверен, как здесь работает расширение, нужно поискать, но спасибо, это действительно полезно.
haridsv

Приятно! Эта опция также, кажется, заставляет ее работать намного быстрее (так как она отключает регулярные выражения).
qwertzguy

15

Во-первых, вам нужно использовать кавычки для специальных символов. Во-вторых, даже в этом случае, grepвы не поймете чередования напрямую; Вы должны будете использовать egrep, или ( grepтолько с GNU ) grep -E.

egrep 'foo|bar' *.txt

(Скобки не нужны, если чередование не является частью большего регулярного выражения.)


4
На самом деле, grep -Eстандартнее, чем egrep.
jw013

8

Если вам не нужны регулярные выражения, это гораздо быстрее использовать fgrepили grep -Fс несколькими параметрами -e, например так:

fgrep -efoo -ebar *.txt

fgrep(альтернативно grep -F) намного быстрее, чем обычный grep, потому что он ищет фиксированные строки вместо регулярных выражений.


4
Пожалуйста, смотрите также комментарии на этой странице, упоминающие, что fgrepэто устарело.
phk

6

Вы можете попробовать приведенную ниже команду, чтобы получить результат:

egrep 'rose.*lotus|lotus.*rose' some_file

3

Дешевый и веселый способ поиска нескольких шаблонов:

$ echo "foo" > ewq ; echo "bar" >> ewq ; grep -H -f ewq *.txt ; rm ewq

Это могло бы принести пользу от объяснения.
Питер Мортенсен

2
Объяснение заключается в том, что -fопция grep принимает файл с несколькими шаблонами. Вместо создания временного файла (который вы можете потом забыть удалить), просто используйте подстановку процесса оболочки:grep -f <(echo foo; echo bar) *.txt
Jakob

3

Pipe ( |) - это специальный символ оболочки, поэтому его необходимо либо экранировать ( \|), либо заключить в кавычки в соответствии с инструкциями manual ( man bash):

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

Заключение символов в двойные кавычки сохраняет буквальное значение всех символов в кавычках

Обратный слэш ( \) без кавычек является escape-символом.

Смотрите: Каких персонажей нужно экранировать в Bash?

Вот несколько примеров (с использованием еще не упомянутых инструментов):

  • Использование ripgrep:

    • rg "foo|bar" *.txt
    • rg -e foo -e bar *.txt
  • Использование git grep:

    • git grep --no-index -e foo --or -e bar

      Примечание. Он также поддерживает логические выражения, такие как --and, --orи --not.

Для операции AND на строку смотрите: Как запустить grep с несколькими шаблонами AND?

Для операции AND для файла см .: Как проверить, существует ли в файле несколько строк или регулярных выражений?


3

У меня были журналы доступа, где даты были тупо отформатированы: [30 / Jun / 2013: 08: 00: 45 +0200]

Но мне нужно было отобразить это как: 30 июня 2013 года 08:00:45

Проблема в том, что, используя «OR» в моем выражении grep, я получал два выражения соответствия в двух отдельных строках.

Вот решение:

grep -in myURL_of_interest  *access.log  | \
grep -Eo '(\b[[:digit:]]{2}/[[:upper:]][[:lower:]]{2}/[[:digit:]]{4}|[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}\b)'   \
| paste - - -d" " > MyAccess.log

2

TL; DR: если вы хотите сделать больше вещей после сопоставления с одним из множества шаблонов, заключите их, как в \(pattern1\|pattern2\)

пример: я хочу найти все места, где переменная, которая содержит имя «date», определяется как String или int. (например, "int cronDate =" или "String textFormattedDateStamp ="):

cat myfile | grep '\(int\|String\) [a-zA-Z_]*date[a-zA-Z_]* =' 

С помощью grep -Eвам не нужно избегать скобок или трубы, т. Е.grep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='


1

Это работает для меня

root@gateway:/home/sshuser# aws ec2 describe-instances --instance-ids i-2db0459d |grep 'STATE\|TAG'

**STATE**   80      stopped

**STATE**REASON     Client.UserInitiatedShutdown    Client.UserInitiatedShutdown: User initiated shutdown

**TAGS**    Name    Magento-Testing root@gateway:/home/sshuser#

1

Есть несколько способов сделать это.

  1. grep 'foo\|bar' *.txt
  2. egrep 'foo|bar' *.txt
  3. find . -maxdepth 1 -type f -name "*.txt" | xargs grep 'foo\|bar'
  4. find . -maxdepth 1 -type f -name "*.txt" | xargs egrep 'foo|bar'

3-й и 4-й вариант будут выполнять grep только в файлах и избегать каталогов с .txtих именами.
Таким образом, в соответствии с вашим вариантом использования, вы можете использовать любой из вариантов, упомянутых выше.
Спасибо!!


0

чтобы добавить к ответу @ geekosaur , если у вас есть несколько шаблонов, которые также содержат вкладки и пробел, вы используете следующую команду

grep -E "foo[[:blank:]]|bar[[:blank:]]"

где [[:blank:]]класс символов RE, представляющий пробел или символ табуляции

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.