Como fazer uma correspondência não gananciosa no grep?

Respostas:

276

Você está procurando uma correspondência não gananciosa (ou preguiçosa). Para obter uma correspondência não gananciosa em expressões regulares, você precisa usar o modificador ?após o quantificador. Por exemplo, você pode mudar .*para .*?.

Por padrão grep, não suporta modificadores não gananciosos, mas você pode usar grep -Ppara usar a sintaxe Perl.

Mark Byers
fonte
3
O modificador eegg: dot all também é conhecido como multilinha. É um modificador que altera o "." combine o comportamento para incluir novas linhas (normalmente não). Não existe esse modificador no grep, mas existe no pcregrep .
A. Wilson
1
Correção: Na maioria dos tipos de expressões regulares que o suportam, o modo que permite .corresponder às novas linhas é chamado DOTALL ou modo de linha única ; Ruby é o único que chama de multilinha . Nos outros tipos, multilinha é o modo que permite que as âncoras ( ^e $) correspondam aos limites da linha. O Ruby não tem um modo equivalente, porque no Ruby eles sempre funcionam dessa maneira.
Alan Moore
5
-Pfoi uma novidade completamente nova para mim, estou felizmente me cumprimentando há anos e usando apenas -E... tantos anos perdidos ! - Nota para si mesmo: releia as páginas de manual como uma coisa regular (ainda mais!), Você nunca digere opções e opções suficientes.
Ocodo 15/08
29
Em algumas plataformas (como o Mac OS X), grepnão há suporte -P, mas se você usar, egreppoderá usar o .*?padrão para obter o mesmo resultado. egrep -o 'start.*?end' text.html
SaltyNuts
4
Como uma extensão ao comentário do @SaltyNuts, o Mac OS X não suporta, -Pmas -Echamaria, egrepportanto, as sugestões sugeridas .*?funcionam perfeitamente.
Fredrik Erlandsson
83

Atualmente, o .*?único funciona perl. Não tenho certeza de qual seria a sintaxe regexp estendida grep equivalente. Felizmente, você pode usar a sintaxe perl com o grep para grep -Pque funcione, mas grep -Eque é o mesmo egrepque não funcionaria (seria ganancioso).

Veja também: http://blog.vinceliu.com/2008/02/non-greedy-regular-expression-matching.html

John Smith
fonte
9
grep -Pnão funciona no GNU grep 2.9 - apenas tentei (isso não acontecer erro, apenas silenciosamente não se aplica a ?Intertestly nem o. Não classe por exemplo:env|grep '[^\=]*\='
roberto tomás
2
Não há grep -Popção ou pgrepcomando no Darwin / OS X 10.8 Mountain Lion, mas egrepfunciona muito bem.
9788 Steve
2
Existe um pgrepcomando na minha caixa do OS X 10.9, mas é um programa completamente diferente cujo objetivo é "localizar ou sinalizar processos por nome".
Desty 11/07
@ robertotomás Respondendo a um comentário de 6 anos aqui, mas .... Eu pensei isso também e depois percebi que estava recebendo várias partidas não gananciosas. Por exemplo, em um terminal colorido, você pode ver que `echo" bbbbb "| grep -P 'b. *? b' 'retorna 2 correspondências.
Zzxyz
12

Meu grep que funciona depois de experimentar coisas neste segmento:

echo "hi how are you " | grep -shoP ".*? "

Apenas certifique-se de acrescentar um espaço a cada uma das suas linhas

(O meu era uma pesquisa linha por linha para cuspir palavras)

jonz
fonte
3
-shoPnice :) mnemônico
Mariusz
echo "bbbbb" | grep -shoP 'b.*?b'é um pouco de uma experiência de aprendizado. A única coisa que funcionou para mim em termos de explicitamente preguiçoso também.
Zzxyz #
12

grep

Para correspondência não gananciosa, grepvocê pode usar uma classe de personagem negada. Em outras palavras, tente evitar caracteres curinga.

Por exemplo, para buscar todos os links para arquivos JPEG no conteúdo da página, você usaria:

grep -o '"[^" ]\+.jpg"'

Para lidar com várias linhas, canalize a entrada xargsprimeiro. Para desempenho, use ripgrep.

kenorb
fonte
3

A resposta curta está usando a próxima expressão regular:

(?s)<car .*? model=BMW .*?>.*?</car>
  • (? s) - isso faz uma correspondência entre várias linhas
  • . *? - combina com qualquer personagem, várias vezes de maneira preguiçosa (correspondência mínima)

Uma resposta (pouco) mais complicada é:

(?s)<([a-z\-_0-9]+?) .*? model=BMW .*?>.*?</\1>

Isso possibilitará a correspondência de car1 e car2 no texto a seguir

<car1 ... model=BMW ...>
...
...
...
</car1>
<car2 ... model=BMW ...>
...
...
...
</car2>
  • (..) representa um grupo de captura
  • \ 1 neste contexto corresponde ao texto do mesmo como correspondido mais recentemente ao capturar o número do grupo 1
jmc
fonte
1

Desculpe, estou com 9 anos de atraso, mas isso pode funcionar para os telespectadores em 2020.

Então, suponha que você tenha uma linha como "Hello my name is Jello". Agora você deseja encontrar as palavras que começam 'H'e terminam 'o'com qualquer número de caracteres no meio. E não queremos linhas, apenas palavras. Então, para isso, podemos usar a expressão:

grep "H[^ ]*o" file

Isso retornará todas as palavras. A maneira como isso funciona é o seguinte: Permitirá todos os caracteres em vez do caractere de espaço entre eles, desta forma, podemos evitar várias palavras na mesma linha.

Agora você pode substituir o caractere de espaço por qualquer outro caractere que desejar. Suponha que a linha inicial era "Hello-my-name-is-Jello", então você pode obter palavras usando a expressão:

grep "H[^-]*o" file
mr.1n5an_e
fonte
0

Eu sei que é um post meio morto, mas notei que isso funciona. Ele removeu a limpeza e a limpeza da minha saída.

> grep -v -e 'clean\-\?up'
> grep --version grep (GNU grep) 2.20
user200850
fonte