Como faço para grep para vários padrões com padrão tendo um caractere de pipe?

501

Eu quero encontrar todas as linhas em vários arquivos que correspondam a um dos dois padrões. Tentei encontrar os padrões que estou procurando digitando

grep (foo|bar) *.txt

mas o shell interpreta o | como um pipe e reclama quando bar não é um executável.

Como posso fazer o grep para múltiplos padrões no mesmo conjunto de arquivos?

    
por Dan 26.04.2012 / 03:03

12 respostas

696

Primeiro, você precisa proteger o padrão da expansão pelo shell. A maneira mais fácil de fazer isso é colocar aspas simples em torno dele. Aspas simples impedem a expansão de qualquer coisa entre elas (incluindo barras invertidas); a única coisa que você não pode fazer é ter aspas simples no padrão.

grep 'foo*' *.txt

Se você precisar de uma aspa simples, poderá escrevê-la como '\'' (string literal, literal, literal de string aberta).

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

Em segundo lugar, o grep suporta duas sintaxes para padrões. A antiga sintaxe padrão ( expressões regulares básicas ) não suporta o operador de alternância ( | ), embora Algumas versões têm isso como uma extensão, mas escritas com uma barra invertida.

grep 'foo\|bar' *.txt

O modo portátil é usar a nova sintaxe, expressões regulares estendidas . Você precisa passar a opção -E para grep para selecioná-la. No Linux, você também pode digitar egrep em vez de grep -E (em outros unices, você pode torná-lo um alias).

grep -E 'foo|bar' *.txt

Outra possibilidade, quando você está apenas procurando por vários padrões (ao invés de construir um padrão complexo usando disjunção) é passar múltiplos padrões para grep . Você pode fazer isso precedendo cada padrão com a opção -e .

grep -e foo -e bar *.txt
    
por 26.04.2012 / 03:13
62
egrep "foo|bar" *.txt

ou

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

Citando seletivamente a página man do 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.

No começo eu não li mais, então não reconheci as diferenças sutis:

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

Eu sempre usei egrep e desnecessariamente parens, porque aprendi com exemplos. Agora eu aprendi algo novo. :)

    
por 26.04.2012 / 03:09
20

Como o TC1 disse, -F parece ser uma opção utilizável:

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

$> patterns="foo
bar" 

$> grep -F "${patterns}" text
foo
bar
    
por 26.04.2012 / 20:46
11

Primeiramente, você precisa usar aspas para caracteres especiais. Segundo, mesmo assim grep não entenderá a alternância diretamente; você precisaria usar egrep ou (com GNU grep somente) grep -E .

egrep 'foo|bar' *.txt

(Os parênteses são desnecessários, a menos que a alternância seja parte de uma regex maior).

    
por 26.04.2012 / 03:06
5

Você pode tentar o comando abaixo para obter o resultado:

egrep 'rose.*lotus|lotus.*rose' some_file
    
por 05.12.2016 / 15:39
5

Se você não precisa de expressões regulares, é muito mais rápido usar fgrep ou grep -F com vários parâmetros -e, assim:

fgrep -efoo -ebar *.txt

fgrep (alternativamente grep -F ) é muito mais rápido que o grep normal porque procura por cadeias fixas em vez de expressões regulares.

    
por 27.12.2016 / 21:02
3

Eu tinha registros de acesso em que as datas estavam estupidamente formatadas: [30 / Jun / 2013: 08: 00: 45 +0200]

Mas eu precisava exibi-lo como: 30 / Jun / 2013 08:00:45

O problema é que usando "OR" na minha instrução grep, eu estava recebendo as duas expressões de correspondência em duas linhas separadas.

Aqui está a solução:

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

Espero que ajude:)

    
por 15.07.2015 / 13:00
2

Uma maneira barata e alegre de usar múltiplos padrões:

$ echo "foo" > ewq ; echo "bar" >> ewq ; grep -H -f ewq *.txt ; rm ewq
    
por 03.02.2017 / 17:17
2

Pipe ( | ) é um caractere de shell especial, portanto, ele precisa ser escapado ( \| ) ou citado de acordo com o manual ( man bash ):

Quoting is used to remove the special meaning of certain characters or words to the shell. It can be used to disable special treatment for special characters, to prevent reserved words from being recognized as such, and to prevent parameter expansion.

Enclosing characters in double quotes preserves the literal value of all characters within the quotes

A non-quoted backslash (\) is the escape character.

Veja: Quais caracteres precisam ser escapados no Bash?

Aqui estão alguns exemplos (usando ferramentas não mencionadas ainda):

  • Usando ripgrep :

    • rg "foo|bar" *.txt
    • rg -e foo -e bar *.txt
  • Usando git grep :

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

      Observação: ele também suporta expressões booleanas como --and , --or e --not .

Para operação AND por linha, consulte: Como executar o grep com vários padrões AND?

Para a operação AND por arquivo, consulte: Como verificar se todas as strings ou expressões regulares estão em um arquivo?

    
por 11.04.2018 / 11:41
1

Isso funciona para mim

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#
    
por 27.07.2016 / 07:13
1

Existem várias maneiras de fazer isso.

  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'

A 3ª e a 4ª opção farão o grep apenas nos arquivos e evitarão que os diretórios tenham .txt em seus nomes.
Assim, de acordo com o seu caso de uso, você pode usar qualquer uma das opções mencionadas acima. Obrigado !!

    
por 10.04.2018 / 07:51
0

TL; DR: se você quiser fazer mais coisas depois de corresponder a um dos vários padrões, coloque-os como em \(pattern1\|pattern2\)

Ninguém mencionou a pesquisa de qualquer um dos vários padrões seguidos por mais padrões , por isso imaginei que o lançaria aqui:

exemplo: Eu quero encontrar todos os lugares onde uma variável que contém o nome 'date' é definida como um String ou int. (ou seja, "int date =" ou "String date ="):

grep '.' -rne '\(int\|String\) [a-zA-Z_]*date[a-zA-Z_]* ='
    
por 10.12.2018 / 21:03