it-swarm-ru.tech

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

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

grep (foo|bar) *.txt

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

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

681
Dan

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

grep -- 'foo*' *.txt

(также обратите внимание на -- end-of-option-marker для остановки некоторых реализаций grep, включая GNU grep, от обработки файла с именем -foo-.txt например (это было бы расширено Shell от *.txt) для использования в качестве опции (хотя здесь следует аргумент, не являющийся опцией)).

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

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

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

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

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

grep -E -- 'foo|bar' *.txt

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

grep -e foo -e bar -- *.txt

Или поместите шаблоны в несколько строк:

grep -- 'foo
bar' *.txt

Или сохраните эти шаблоны в файле, по одному на строку и запустите

grep -f that-file -- *.txt

Обратите внимание, что если *.txt расширяется до одного файла, grep не будет ставить префикс совпадающих строк с его именем, как при наличии более одного файла. Чтобы обойти это, в некоторых реализациях grep, таких как GNU grep, вы можете использовать -H или с любой реализацией вы можете передать /dev/null как дополнительный аргумент.


_ Некоторые реализации grep поддерживают даже больше, чем Perl-совместимые с -P, или дополненные с -X, -K для подстановочных знаков ksh ...

² в то время как egrep устарела в POSIX и иногда больше не встречается в некоторых системах, в некоторых других системах, таких как Solaris, когда утилиты POSIX или GNU не установлены, то egrep ваш единственный вариант, так как /bin/grep не поддерживает ни одного из -e, -f, -E, \| или многострочные шаблоны

929
Gilles 'SO- stop being evil'
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 и ненужные парены, потому что я учился на примерах. Теперь я узнал что-то новое. :)

109
user unknown

Как сказал TC1, -F кажется приемлемым вариантом:

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

$> patterns="foo
bar" 

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

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

egrep 'foo|bar' *.txt

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

17
geekosaur

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

fgrep -efoo -ebar *.txt

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

8
Moustafa Elqabbany

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

egrep 'rose.*Lotus|lotus.*rose' some_file
6
Abhishek

Труба (|) является специальным символом оболочки, поэтому его необходимо либо экранировать (\|) или указано в соответствии с руководством ( 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 для файла см .: Как проверить, что в файле есть несколько строк или регулярных выражений?

4
kenorb

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

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

У меня были журналы доступа, где даты были тупо отформатированы: [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
3
tsmets

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_]* ='

2
jeremysprofile

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

[email protected]:/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 [email protected]:/home/sshuser#
1
Mansur Ali

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

  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-й вариант будут работать только в файлах и избегать каталогов, имеющих .txt в их именах.
Таким образом, согласно вашему варианту использования, вы можете использовать любой из вариантов, упомянутых выше.
Спасибо!!

1
Bhagyesh Dudhediya

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

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

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

1
Fuseteam