Eu respondi a esta pergunta no SuperUser que era algo relacionado ao tipo de expressões regulares usadas durante o grep de uma saída.
A resposta que dei foi esta:
tail -f log | grep "some_string.*some_string"
E então, em três comentários à minha resposta , @Bob escreveu isto:
.*
é ganancioso e pode capturar mais do que você deseja..*?
geralmente é melhor.
Então isso,
o
?
é um modificador em*
, tornando-o preguiçoso em vez do padrão ganancioso. Assumindo PCRE.
Eu pesquisei por PCRE
, mas não consegui entender qual é o significado disso na minha resposta?
e finalmente isso,
Também devo apontar que isso é regex (grep fazendo POSIX regex por padrão), não um shell glob.
Eu só sei o que é um Regex e o uso muito básico dele no comando grep. Então, não consegui nenhum desses 3 comentários e tenho essas perguntas em mente:
- Quais são as diferenças no uso de
.*?
vs..*
? - Qual é melhor e em que circunstâncias? Forneça exemplos.
Também seria útil entender os comentários, se alguém pudesse
ATUALIZAÇÃO: Como resposta à pergunta Como o Regex é diferente do Shell Globs? @Kusalananda forneceu este link em seu comentário.
NOTA: Se necessário, leia minha resposta a esta pergunta antes de responder para se referir ao contexto.
Suponha que eu pegue uma string como:
can cats eat plants?
O uso de guloso
c.*s
irá corresponder a toda a string desde que comecec
e termine coms
, sendo um operador guloso ele continua a corresponder até a ocorrência final de s.Considerando que usar o lazy
c.*?s
irá corresponder apenas até que a primeira ocorrência des
seja encontrada, ou seja, stringcan cats
.A partir do exemplo acima, você pode deduzir que:
"Greedy" significa corresponder à string mais longa possível. "Preguiçoso" significa corresponder à string mais curta possível. Adicionar a
?
a um quantificador como*
,+
,?
ou{n,m}
torna-o preguiçoso.Ashok já apontou a diferença entre
.*
e.*?
, então vou apenas fornecer algumas informações adicionais.grep
(assumindo a versão GNU) suporta 4 maneiras de combinar strings:-F
opção-E
opção-P
opção em GNU grepgrep
usa BRE por padrão.BRE e ERE estão documentados no capítulo Expressões Regulares do POSIX e PCRE está documentado em seu site oficial . Observe que os recursos e a sintaxe podem variar entre as implementações.
Vale dizer que nem o BRE nem o ERE suportam a preguiça :
Portanto, se você quiser usar esse recurso, precisará usar o PCRE:
.*
é usado para corresponder ao padrão 1 "mais longo" possível..*?
é usado para corresponder ao padrão 1 "mais curto" possível.Na minha experiência, o comportamento mais desejado é geralmente o segundo.
Por exemplo, digamos que temos a seguinte string e queremos apenas corresponder às tags html 2 , não ao conteúdo entre elas:
Agora compare
.*
com.*?
:1. O significado de "mais longo" e "mais curto" em um contexto regex é um pouco complicado, como Kusalananda apontou . Consulte a documentação oficial para obter mais informações.
2. Não é recomendado analisar html com regex . Este é apenas um exemplo para fins educacionais, não o use em produção.
Uma string pode ser correspondida de várias maneiras (do simples ao mais complexo):
Como uma string estática (assume var='Hello World!'):
shell
[ "$var" = "Hello World!" ] && echo yes
grep
echo "$var" | grep -F "Hello"
bash
grep -F "Hello" <<<"$var"
Como um globo:
shell
echo ./*
# lista todos os arquivos em pwd.
shell
case $var in (*Worl*) echo yes;; (*) echo no;; esac
bash
[[ "$var" == *"Worl"* ]] && echo yes
Existem globs básicos e estendidos. O
case
exemplo usa globs básicos. O exemplo bash[[
usa globs estendidos. A primeira correspondência de arquivo pode ser básica ou estendida em algum shell, como configuraçãoextglob
no bash. Ambos são idênticos neste caso. Grep não pôde usar globs.O asterisco em um glob significa algo diferente de um asterisco em um regex :
glob
* matches any number (including none) of
quaisquer caracteres . elemento precedenteregex .
* matches any number (including none) of the
Como uma expressão regular básica (BRE):
sed
echo "$var" | sed 's/W.*d//'
# imprimir: Olá!
grep
grep -o 'W.*d' <<<"$var"
# print Mundo !
Não há BRE em shells (básicos) ou awk.
Expressões regulares estendidas (ERE):
bash
[[ "$var" =~ (H.*l) ]]
# match: Hello Worl
sed
echo "$var" | sed -E 's/(d|o)//g'
# print: Hell Wrl!
awk
awk '/W.*d/{print $1}' <<<"$var"
# print: Hello
grep
grep -oE 'H.*l' <<<"$var"
# print: Hello Worl
Expressões regulares compatíveis com Perl:
grep
grep -oP 'H.*?l
# imprimir: Hel
Somente em um PCRE a
*?
tem algum significado de sintaxe específico.Isso torna o asterisco preguiçoso (sem ganância): preguiça em vez de ganância .
Isso é só a ponta do iceberg, existem gananciosos, preguiçosos e dóceis ou possessivos . Há também lookahead e lookbehind, mas esses não se aplicam ao asterisco
*
.Existe uma alternativa para obter o mesmo efeito de um regex não ganancioso:
A ideia é muito simples: não use um ponto
.
, negue o próximo caractere correspondente[^o]
. Com uma etiqueta da web:O acima deve esclarecer completamente todos os comentários do @Bob 3. Parafraseando:
.*
é ganancioso.*?
não é.Perguntas
Quais são as diferenças no uso de . ? vs. ?
.*?
é válido apenas na sintaxe PCRE..*
é mais portátil.[^a]*
Qual é melhor e em que circunstâncias? Forneça exemplos.
Melhor? Depende do objetivo. Não há melhor, cada um é útil para propósitos diferentes. Eu forneci vários exemplos acima. Precisa de mais?