Se eu usar grep em um documento que contenha o seguinte:
ThisExampleString
...para a expressão This*String
or *String
, nada é retornado. No entanto, This*
retorna a linha acima conforme esperado.
Se a expressão está entre aspas não faz diferença.
Achei que o asterisco indicava algum número de caracteres desconhecidos? Por que só funciona se estiver no início da expressão? Se este for o comportamento pretendido, o que devo usar em vez das expressões This*String
e *String
?
Responder1
Um asterisco emexpressões regularessignifica "corresponder ao elemento anterior 0 ou mais vezes".
No seu caso particular com grep 'This*String' file.txt
, você está tentando dizer "ei, grep, combine-me a palavra Thi
, seguida de s
zero ou mais letras minúsculas, seguida da palavra String
". As letras minúsculas s
não podem ser encontradas em nenhum lugar Example
, portanto, grep ignora ThisExampleString
.
No caso de grep '*String' file.txt
, você está dizendo "grep, combine-me com a string vazia - literalmente nada - que precede a palavra String
". Claro, não é assim ThisExampleString
que se deve ler. (Háoutros significados possíveis--você pode tentar isso com e sem a -E
bandeira--mas nenhum dos significados é parecido com o que você realmente deseja aqui.)
Sabendo que isso .
significa "qualquer caractere único", poderíamos fazer o seguinte: grep 'This.*String' file.txt
. Agora o comando grep irá lê-lo corretamente: This
seguido por qualquer caractere (pense nisso como uma seleção de caracteres ASCII) repetido qualquer número de vezes, seguido por String
.
Responder2
O *
metacaractere em BRE 1 s, ERE 1 s e PCRE 1 s corresponde a 0 ou mais ocorrências do padrão agrupado anteriormente (se um padrão agrupado precede o *
metacaractere), 0 ou mais ocorrências da classe de caracteres anterior (se uma classe de caracteres for precedendo o *
metacaractere) ou 0 ou mais ocorrências do caractere anterior (se nem um padrão agrupado nem uma classe de caracteres precedem o *
metacaractere);
Isso significa que no This*String
padrão, sendo o *
metacaractere não precedido por um padrão agrupado ou por uma classe de caracteres, o *
metacaractere corresponde a 0 ou mais ocorrências do caractere anterior (neste caso o s
caractere):
% cat infile
ThisExampleString
ThisString
ThissString
% grep 'This*String' infile
ThisString
ThissString
Para corresponder 0 ou mais ocorrências de qualquer caractere, você deseja corresponder 0 ou mais ocorrências do .
metacaractere, que corresponde a qualquer caractere:
% cat infile
ThisExampleString
% grep 'This.*String' infile
ThisExampleString
O *
metacaractere em BREs e EREs é sempre "ganancioso", ou seja, corresponderá à correspondência mais longa:
% cat infile
ThisExampleStringIsAString
% grep -o 'This.*String' infile
ThisExampleStringIsAString
Este pode não ser o comportamento desejado; caso não esteja, você pode ligar grep
o mecanismo PCRE de (usando a -P
opção) e anexar o ?
metacaractere, que quando colocado após os *
metacaracteres +
e tem o efeito de alterar sua ganância:
% cat infile
ThisExampleStringIsAString
% grep -Po 'This.*?String' infile
ThisExampleString
1: Expressões regulares básicas, expressões regulares estendidas e expressões regulares compatíveis com Perl
Responder3
Uma das explicações encontradas aquilink:
O asterisco "
*
" não significa a mesma coisa em expressões regulares e em curingas; é um modificador que se aplica ao caractere único anterior ou expressão como [0-9]. Um asterisco corresponde a zero ou mais do que o precede. Assim,[A-Z]*
corresponde a qualquer número de letras maiúsculas, incluindo nenhuma, enquanto[A-Z][A-Z]*
corresponde a uma ou mais letras maiúsculas.
Responder4
*
tem um significado especial tanto como conchaglobulandocaractere ("curinga") e como expressão regularmetacaractere. Você deve levar ambos em consideração, embora se vocêcitarsua expressão regular, então você pode evitar que o shell a trate de maneira especial e garantir que ela seja transmitida inalterada paragrep
. Emboratipo desemelhante conceitualmente, o que *
significa para o shell é bem diferente do que significa para grep
.
Primeiroo shell trata *
como um curinga.
Você disse:
Se a expressão está entre aspas não faz diferença.
Isso depende de quais arquivos existem em qualquer diretório em que você esteja quando executa o comando. Para padrões que contêm o separador de diretórios /
, isso pode depender de quais arquivos existem em todo o sistema. Você deveria semprecitarexpressões regulares para grep
--easpas simplesgeralmente são os melhores -a menos quevocê tem certeza de que está bem com onove tipos de transformações potencialmente surpreendenteso shell executa de outra formaantesexecutando o grep
comando.
Quando o shell encontra um *
personagem que não écitado, significa "zero ou mais de qualquer caractere" esubstitui a palavra que o contémcom uma lista de nomes de arquivos que correspondem ao padrão. (Nomes de arquivos que começam com .
são excluídos - a menos que seu próprio padrão comece com.
ouvocê configurou seu shell para incluí-los de qualquer maneira.) Isso é conhecido comoglobulando--e também pelos nomesexpansão do nome do arquivoeexpansão do nome do caminho.
O efeito grep
geralmente será que o primeiro nome de arquivo correspondente seja considerado como a expressão regular - mesmo que seja bastante óbvio para um leitor humano que énãosignifica uma expressão regular - enquanto todos os outros nomes de arquivos listados automaticamente em seu globo são considerados os arquivosdentroonde procurar correspondências. (Você não vê a lista - ela é passada de forma opaca para grep
.) Você praticamente nunca quer que isso aconteça.
A razão pela qual isso éàs vezesnão é um problema - e no seu caso particular, pelo menosaté aqui, não foi - é que *
será deixado em pazse todas as afirmações a seguir forem verdadeiras:
Havianãoarquivos cujos nomes correspondiam. ...Ouvocê desativou o globbing em seu shell, normalmente com
set -f
ou equivalenteset -o noglob
. Mas isso é incomum e você provavelmente saberia que fez isso.Você está usando um shell cujo comportamento padrão é deixar
*
de lado quando não há nomes de arquivos correspondentes. Este é o caso do Bash, que você estáprovavelmenteusando, mas não em todos os shells do estilo Bourne. (O comportamento padrão no popular shell Zsh, por exemplo, é que os globs(a)expandir ou(b)produzir um erro.)...Ouvocê alterou esse comportamento do seu shell - a forma como isso é feito varia entre os shells.Você não temde outra formadisse ao seu shell para permitir que globs fossem substituídos pornadaquando não há arquivos correspondentes, nem falhar com uma mensagem de erro nesta situação. No Bash, isso teria sido feito habilitando o
nullglob
oufailglob
opção de shell, respectivamente.
Às vezes você pode confiar nos números 2 e 3, mas raramente pode confiar no número 1. Um grep
comando com um padrão sem aspas que funciona agora pode parar de funcionar quando você tiver arquivos diferentes ou quando for executado em um local diferente.Cite sua expressão regular e o problema desaparece.
Entãoo grep
comando trata *
como um quantificador.
As outras respostas - como aquelaspor Sergiy Kolodyazhnyyepor Kos--também abordam este aspecto desta questão, de maneiras um pouco diferentes. Portanto, encorajo aqueles que ainda não os leram a fazê-lo, antes ou depois de ler o restante desta resposta.
Supondo que *
chegue ao grep - o que a citação deve garantir - grep
então isso significa queo item que o precedepode ocorrer inúmeras vezes, em vez de ter que ocorrer exatamente uma vez. Ainda pode ocorrer uma vez. Ou pode não estar presente. Ou poderia ser repetido. Texto que combina comqualquerdessas possibilidades serão correspondidas.
O que quero dizer com "item"?
Um únicopersonagem. Como
b
corresponde a um literalb
,b*
corresponde a zero ou maisb
s,ab*c
correspondendo , portantoac
, aabc
,,,, etc.abbc
abbbc
Da mesma forma, desde
.
corresponde a qualquer caractere,.*
corresponde a zero ou mais caracteres1, portantoa.*c
corresponde aac
,akc
,ahjglhdfjkdlgjdfkshlgc
, paracccccchjckhcc
, etc.OuAclasse de personagem. Como
[xy]
corresponde ax
ouy
,[xy]*
corresponde a zero ou mais caracteres onde cada um éx
ouy
, portantop[xy]*q
corresponde apq
,pxq
,pyq
,pxxq
,pxyq
,pyxq
,pyyq
,pxxxq
,pxxyq
, etc.Isto também se aplica aformas abreviadasde classes de caracteres como
\w
,\W
,\s
e\S
. Como\w
corresponde a qualquer caractere de palavra,\w*
corresponde a zero ou mais caracteres de palavra.OuAgrupo. Como
\(bar\)
corresponde abar
,\(bar\)*
corresponde a zero ou maisbar
s, portantofoo\(bar\)*baz
corresponde afoobaz
,foobarbaz
,foobarbarbaz
,foobarbarbarbaz
, etc.Com as opções
-E
ou-P
,grep
trata sua expressão regular como umaANTESouPCRErespectivamente, e não como umBRE, e os grupos são cercados por(
)
em vez de\(
\)
, então você usaria(bar)
em vez de\(bar\)
efoo(bar)baz
em vez defoo\(bar\)baz
.
man grep
fornece uma explicação razoavelmente acessível da sintaxe BRE e ERE no final, bem como lista todas as opções de linha de comando grep
aceitas no início. Eu recomendo essa página de manual como recurso, e tambéma documentação do GNU Grepeeste tutorial/site de referência(que vinculei a várias páginas acima).
Para testar e aprender grep
, recomendo chamá-lo com um padrão, mas sem nome de arquivo. Em seguida, ele recebe informações do seu terminal. Insira linhas; as linhas que são ecoadas de volta para você são aquelas que continham o texto correspondente ao seu padrão. Para sair, pressione Ctrl+ Dno início de uma linha, que sinaliza o fim da entrada. (Ou você pode pressionar Ctrl+ Ccomo acontece com a maioria dos programas de linha de comando.) Por exemplo:
grep 'This.*String'
Se você usar a --color
bandeira, grep
irá destacar o específicopeçasde suas linhas que correspondem à sua expressão regular, o que é muito útil tanto para descobrir o que uma expressão regular faz quanto para encontrar o que você está procurando depois de fazer isso. Por padrão, os usuários do Ubuntu têm um alias Bash que faz com grep --color=auto
que seja executado - o que é suficiente para esse propósito - quando você executa grep
a partir da linha de comando, então provavelmente nem precisará passar --color
manualmente.
1 Portanto, .*
em uma expressão regular significa o que *
significa em um shell glob. Porém, a diferença é que grep
imprime automaticamente as linhas que contêm sua correspondênciaem qualquer lugarneles, por isso normalmente é desnecessário ter .*
no início ou no final de uma expressão regular.