desenv-web-rp.com

Como grep para vários padrões com padrão com um caractere de barra vertical?

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 grep para vários padrões no mesmo conjunto de arquivos?

681
Dan

Primeiro, você precisa proteger o padrão da expansão pelo Shell. A maneira mais fácil de fazer isso é colocar aspas simples. 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

(observe também o -- marcador de fim de opção para interromper algumas implementações de grep incluindo GNU grep de tratar um arquivo chamado -foo-.txt por exemplo (que seria expandido pelo Shell a partir de *.txt) deve ser tomado como uma opção (mesmo que siga um argumento de não opção aqui)).

Se você precisar de uma única citação, poderá escrevê-la como '\'' (literal da string final, citação literal, literal da string aberta).

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

Segundo, o grep suporta pelo menos duas sintaxes para padrões. A sintaxe padrão antiga ( expressões regulares básicas ) não suporta a alternância (|), embora algumas versões o tenham como extensão, mas escritas com uma barra invertida.

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

A maneira portátil é usar a sintaxe mais recente, expressões regulares estendidas . Você precisa passar o -E opção para grep para selecioná-lo (anteriormente isso foi feito com o comando egrep separado²)

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

Outra possibilidade quando você está apenas procurando por um dos vários padrões (em vez de criar um padrão complexo usando disjunção) é passar vários padrões para grep. Você pode fazer isso precedendo cada padrão com o -e opção.

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

Ou coloque padrões em várias linhas:

grep -- 'foo
bar' *.txt

Ou armazene esses padrões em um arquivo, um por linha e execute

grep -f that-file -- *.txt

Observe que se *.txt expande para um único arquivo, grep não prefixa as linhas correspondentes com seu nome, como acontece quando há mais de um arquivo. Para contornar isso, com algumas implementações de grep como GNU grep, você pode usar o -H opção, ou com qualquer implementação, você pode passar /dev/null como um argumento extra.


¹ algumas implementações grep suportam ainda mais como as compatíveis com Perl com -P, ou aumentado com -X, -K para curingas ksh ...

² enquanto egrep foi descontinuado pelo POSIX e às vezes não é mais encontrado em alguns sistemas, em outros sistemas como o Solaris quando o POSIX ou GNU não foram instalados, então egrep é sua única opção como sua /bin/grep não suporta nenhum dos -e, -f, -E, \| ou padrões de várias linhas

929
egrep "foo|bar" *.txt

ou

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

citando seletivamente a página de manual 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, 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. :)

109
user unknown

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
25

Primeiro, 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 apenas) grep -E.

egrep 'foo|bar' *.txt

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

17
geekosaur

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 comum, porque procura por seqüências fixas em vez de expressões regulares.

8
Moustafa Elqabbany

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

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

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

A citação é usada para remover o significado especial de certos caracteres ou palavras do Shell. Ele pode ser usado para desativar o tratamento especial de caracteres especiais, impedir que palavras reservadas sejam reconhecidas como tal e impedir a expansão de parâmetros.

O fechamento de caracteres entre aspas duplas preserva o valor literal de todos os caracteres entre aspas

Uma barra invertida não citada (\) é o caractere de escape.

Veja: Quais caracteres precisam ser escapados no Bash?

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

  • Usando ripgrep :

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

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

      Nota: 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 operação AND por arquivo, consulte: Como verificar se existem várias seqüências de caracteres ou expressões regulares em um arquivo?

4
kenorb

Uma maneira barata e alegre de grep para vários padrões:

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

Eu tinha logs de acesso em que as datas eram 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 declaraçã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
3
tsmets

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

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

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

Com grep -E, Você não precisa escapar dos parênteses ou do tubo, ou seja, grep -E '(int|String) [a-zA-Z_]*date[a-zA-Z_]* ='

2
jeremysprofile

Isso funciona para mim

[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

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 cumprem apenas nos arquivos e evitam que os diretórios tenham .txt em seus nomes.
Portanto, conforme seu caso de uso, você pode usar qualquer uma das opções mencionadas acima.
Obrigado!!

1
Bhagyesh Dudhediya

para adicionar a resposta do @ geekosaur , se você tiver vários padrões que também contenham tabulações e espaço, use o seguinte comando

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

onde [[:blank:]] é a classe de caracteres RE que representa um espaço ou um caractere de tabulação

1
Fuseteam