Como usar expressões regulares (regexes) no Linux

Um laptop mostrando um terminal Linux com linhas de texto verde.
Fatmawati Achmad Zaenuri / Shutterstock

Quer saber o que essas estranhas sequências de símbolos fazem no Linux? Eles oferecem magia de linha de comando! Vamos ensiná-lo a lançar feitiços de expressão regular e aumentar o nível de suas habilidades de linha de comando.

O que são expressões regulares?

Expressões regulares ( regexes ) são uma maneira de encontrar sequências de caracteres correspondentes. Eles usam letras e símbolos para definir um padrão que é pesquisado em um arquivo ou fluxo. Existem vários sabores diferentes de regex. Vamos dar uma olhada na versão usada em utilitários e comandos comuns do Linux, como  grepo comando que imprime linhas que correspondem a um padrão de pesquisa .

Livros inteiros foram escritos sobre regexes, então este tutorial é apenas uma introdução. Existem regexes básicos e estendidos, e usaremos os estendidos aqui.

Para usar as expressões regulares estendidas com grep, você deve usar a -Eopção (estendida). Como isso fica cansativo muito rapidamente, o egrepcomando foi criado. egrepcomando é o mesmo que a grep -Ecombinação, você apenas não precisa usar a -Eopção todas as vezes.

Se você achar mais conveniente usar egrep, você pode. No entanto, esteja ciente de que ele está oficialmente obsoleto. Ainda está presente em todas as distribuições que verificamos, mas pode desaparecer no futuro.

Claro, você sempre pode fazer seus próprios aliases, portanto, suas opções favoritas estão sempre incluídas para você.

De Pequenos Começos

Para nossos exemplos, usaremos um arquivo de texto simples contendo uma lista de Geeks. Lembre-se de que você pode usar regexes com muitos comandos do Linux. Estamos usando apenas  grep uma maneira conveniente de demonstrá-los.

Aqui está o conteúdo do arquivo:

menos geek.txt

O comando "less geek.txt" em uma janela de terminal.

A primeira parte do arquivo é exibida.

Saída do comando "less geek.txt" em uma janela de terminal.

Vamos começar com um padrão de pesquisa simples e procurar ocorrências da letra “o” no arquivo. Novamente, como estamos usando a opção -E(regex estendido) em todos os nossos exemplos, digitamos o seguinte:

grep -E 'o' geeks.txt

O comando "grep -E 'o' geeks.txt" em uma janela de terminal.

Cada linha que contém o padrão de pesquisa é exibida e a letra correspondente é destacada. Fizemos uma pesquisa simples, sem restrições. Não importa se a letra aparece mais de uma vez, no final da string, duas vezes na mesma palavra ou mesmo ao lado dela.

Alguns nomes tinham dois O’s; digitamos o seguinte para listar apenas aqueles:

grep -E 'oo' geeks.txt

O comando "grep -E 'oo' geeks.txt" em uma janela de terminal.

Nosso conjunto de resultados, como esperado, é muito menor e nosso termo de pesquisa é interpretado literalmente. Não significa nada além do que digitamos: caracteres duplos “o”.

Veremos mais funcionalidade com nossos padrões de pesquisa à medida que avançamos.

Números de linha e outros truques grep

Se quiser  grep listar o número da linha das entradas correspondentes, você pode usar a opção -n(número da linha). Isso é um  greptruque – não faz parte da funcionalidade do regex. No entanto, às vezes, você pode querer saber onde em um arquivo as entradas correspondentes estão localizadas.

Nós digitamos o seguinte:

grep -E -n 'o' geeks.txt

O comando "grep -E -n 'o' geeks.txt" em uma janela de terminal.

Outro greptruque útil que  você pode usar é a opção -o(apenas correspondência). Ele exibe apenas a sequência de caracteres correspondente, não o texto ao redor. Isso pode ser útil se você precisar examinar rapidamente uma lista em busca de correspondências duplicadas em qualquer uma das linhas.

Para fazer isso, digitamos o seguinte:

grep -E -n -o 'o' geeks.txt

O comando "grep -E -n -o 'o' geeks.txt" em uma janela de terminal.

Se você quiser reduzir a produção ao mínimo, pode usar a -copção (contagem).

Nós digitamos o seguinte para ver o número de linhas no arquivo que contém correspondências:

grep -E -c 'o' geeks.txt

O comando "grep -E -c 'o' geeks.txt" em uma janela de terminal.

O Operador de Alternação

Se você quiser pesquisar ocorrências de “l” duplo e “o” duplo, você pode usar o |caractere barra vertical ( ), que é o operador de alternância. Ele procura correspondências para o padrão de pesquisa à sua esquerda ou direita.

Nós digitamos o seguinte:

grep -E -n -o 'll | oo' geeks.txt

O "grep -E -n -o 'll | oo' geeks.txt" em uma janela de terminal.

Qualquer linha contendo um duplo “l”, “o” ou ambos, aparece nos resultados.

Sensibilidade ao Caso

Você também pode usar o operador de alternância para criar padrões de pesquisa, como este:

sou | sou

Isso corresponderá a “am” e “Am”. Para qualquer coisa diferente de exemplos triviais, isso leva rapidamente a padrões de pesquisa complicados. Uma maneira fácil de contornar isso é usar a opção -i(ignorar maiúsculas e minúsculas) com grep.

Para fazer isso, digitamos o seguinte:

grep -E 'am' geeks.txt
grep -E -i 'am' geeks.txt

Os comandos "grep -E 'am' geeks.txt" e "grep -E -i 'am' geeks.txt" em uma janela de terminal.

O primeiro comando produz três resultados com três correspondências destacadas. O segundo comando produz quatro resultados porque o “Am” em “Amanda” também corresponde.

Recomendado:  Como alterar o editor crontab padrão

Ancoragem

Podemos combinar a sequência “Am” de outras maneiras também. Por exemplo, podemos pesquisar esse padrão especificamente ou ignorar o caso e especificar que a sequência deve aparecer no início de uma linha.

Quando você combina sequências que aparecem na parte específica de uma linha de caracteres ou de uma palavra, isso é chamado de ancoragem. Use o ^símbolo circunflexo ( ) para indicar que o padrão de pesquisa só deve considerar uma sequência de caracteres uma correspondência se ela aparecer no início de uma linha.

Nós digitamos o seguinte (observe que o circunflexo está entre aspas simples):

grep -E ‘Am’ geeks.txt

grep -E -i '^ am' geeks.txt

Os comandos "grep -E 'Am' geeks.txt" e "grep -E -i '^ am' geeks.txt" em uma janela de terminal.

Ambos os comandos correspondem a “Am”.

Agora, vamos procurar linhas que contenham um duplo “n” no final de uma linha.

Digitamos o seguinte, usando um cifrão ( $) para representar o final da linha:

grep -E -i 'nn' geeks.txt
grep -E -i 'nn $' geeks.txt

Os comandos "grep -E -i 'nn' geeks.txt" e "grep -E -i 'nn $' geeks.txt" em uma janela de terminal.

Curingas

Você pode usar um ponto ( .) para representar qualquer caractere único.

Digitamos o seguinte para pesquisar padrões que começam com “T”, terminam com “m” e têm um único caractere entre eles:

grep -E 'Tm' geeks.txt

O comando "grep -E 'Tm' geeks.txt" em uma janela de terminal.

O padrão de pesquisa correspondeu às sequências “Tim” e “Tom”. Você também pode repetir os pontos para indicar um certo número de caracteres.

Digitamos o seguinte para indicar que não nos importamos quais são os três caracteres do meio:

grep-E 'J ... n' geeks.txt

O comando "grep-E 'J ... n' geeks.txt" em uma janela de terminal.

A linha contendo “Jason” é correspondida e exibida.

Use o asterisco ( *) para corresponder a zero ou mais ocorrências do caractere anterior. Neste exemplo, o caractere que precederá o asterisco é o ponto ( .), que (novamente) significa qualquer caractere.

Isso significa que o asterisco ( *) corresponderá a qualquer número (incluindo zero) de ocorrências de qualquer caractere.

O asterisco às vezes é confuso para iniciantes em regex. Talvez seja porque eles geralmente o usam como um caractere curinga que significa “qualquer coisa”.

Em expressões regulares, porém,  'c*t' não corresponde a “gato”, “cot,” “galeirão”, etc. Em vez disso, se traduz como “corresponde a zero ou mais caracteres ‘c’, seguidos por um ‘t’”. Portanto, corresponde a “t”, “ct”, “cct”, “ccct” ou qualquer número de caracteres “c”.

Como sabemos o formato do conteúdo em nosso arquivo, podemos adicionar um espaço como o último caractere no padrão de pesquisa. Um espaço só aparece em nosso arquivo entre o nome e o sobrenome.

Portanto, digitamos o seguinte para forçar a pesquisa a incluir apenas os primeiros nomes do arquivo:

grep -E 'J. * n' geeks.txt
grep -E 'J. * n' geeks.txt

Os comandos "grep -E 'J. * n' geeks.txt" e "grep -E 'J. * n' geeks.txt" em uma janela de terminal.

À primeira vista, os resultados do primeiro comando parecem incluir algumas correspondências estranhas. No entanto, todos eles correspondem às regras do padrão de pesquisa que usamos.

A sequência deve começar com um “J” maiúsculo, seguido por qualquer número de caracteres e, em seguida, um “n”. Ainda assim, embora todas as correspondências comecem com “J” e terminem com “n”, algumas delas não são o que você esperava.

Como adicionamos o espaço no segundo padrão de pesquisa, obtivemos o que pretendíamos: todos os primeiros nomes que começam com “J” e terminam com “n”.

Classes de personagens

Digamos que desejamos encontrar todas as linhas que começam com “N” ou “W” maiúsculo.

Se usarmos o seguinte comando, ele corresponde a qualquer linha com uma sequência que começa com “N” ou “W” maiúsculo, não importa onde apareça na linha:

grep -E 'N | W' geeks.txt

Não é isso que queremos. Se aplicarmos a âncora de início de linha ( ^) no início do padrão de pesquisa, conforme mostrado abaixo, obteremos o mesmo conjunto de resultados, mas por um motivo diferente:

grep -E '^ N | W' geeks.txt

Os comandos "grep -E 'N | W' geeks.txt" e "grep -E '^ N | W' geeks.txt" em uma janela de terminal.

A pesquisa corresponde a linhas que contêm um “W” maiúsculo, em qualquer lugar da linha. Também corresponde à linha “Chega” porque começa com “N” maiúsculo. A âncora de início de linha ( ^) é aplicada apenas ao “N” maiúsculo

Também poderíamos adicionar uma âncora de início de linha a “W” maiúsculo, mas isso logo se tornaria ineficiente em um padrão de pesquisa mais complicado do que nosso exemplo simples.

A solução é colocar parte de nosso padrão de pesquisa entre colchetes ( []) e aplicar o operador âncora ao grupo. Os colchetes ( []) significam “qualquer caractere desta lista”. Isso significa que podemos omitir o |operador de alternância ( ) porque não precisamos dele.

Podemos aplicar a âncora de início de linha a todos os elementos da lista entre colchetes ( []). (Observe que a âncora do início da linha está fora dos colchetes).

Digitamos o seguinte para pesquisar qualquer linha que comece com “N” ou “W” maiúsculo:

grep -E '^ [NW]' geeks.txt

O comando grep -E '^ [NW]' geeks.txt "em uma janela de terminal.

Usaremos esses conceitos no próximo conjunto de comandos também.

Recomendado:  Como ligar e desligar o seu iPhone sem usar o botão liga / desliga

Nós digitamos o seguinte para pesquisar qualquer pessoa chamada Tom ou Tim:

grep -E 'T [oi] m' geeks.txt

Se o acento circunflexo ( ^) for o primeiro caractere entre colchetes ( []), o padrão de pesquisa procura por qualquer caractere que não apareça na lista.

Por exemplo, digitamos o seguinte para procurar qualquer nome que comece com “T”, termine com “m” e em que a letra do meio não seja “o”:

grep -E 'T [^ o] m' geeks.txt

Podemos incluir qualquer número de caracteres na lista. Digitamos o seguinte para procurar nomes que começam com “T”, terminam em “m” e contêm qualquer vogal no meio:

grep -E 'T [aeiou] m' geeks.txt

Os comandos "grep -E 'T [oi] m' geeks.txt" e "grep -E 'T [aeiou] m' geeks.txt" em uma janela de terminal.

Expressões de intervalo

Você pode usar expressões de intervalo para especificar o número de vezes que deseja que o caractere ou grupo anterior seja encontrado na string correspondente. Você coloca o número entre chaves ( {}).

Um número por si só significa especificamente esse número, mas se você segui-lo com uma vírgula ( ,), significa esse número ou mais. Se você separar dois números com uma vírgula ( 1,2), significa o intervalo de números do menor ao maior.

Queremos procurar nomes que começam com “T”, são seguidos por pelo menos uma, mas não mais do que duas, vogais consecutivas e terminam em “m”.

Então, digitamos este comando:

grep -E 'T [aeiou] {1,2} m' geeks.txt

O comando "grep -E 'T [aeiou] {1,2} m' geeks.txt" em uma janela de terminal.

Isso corresponde a “Tim”, “Tom” e “Equipe”.

Se quisermos pesquisar a sequência “el”, digitamos o seguinte:

grep -E 'el' geeks.txt

Adicionamos um segundo “l” ao padrão de pesquisa para incluir apenas as sequências que contêm “l” duplo:

grep -E 'ell' geeks.txt

Isso é equivalente a este comando:

grep -E 'el {2}' geeks.txt

Se fornecermos um intervalo de “pelo menos uma e não mais do que duas” ocorrências de “l”, ela corresponderá às sequências “el” e “ell”.

Isso é sutilmente diferente dos resultados do primeiro desses quatro comandos, em que todas as correspondências eram para sequências “el”, incluindo aquelas dentro das sequências “ell” (e apenas um “l” é destacado).

Nós digitamos o seguinte:

grep -E 'el {1,2}' geeks.txt

O comando "grep -E 'el' geeks.txt" em uma janela de terminal.

Para encontrar todas as sequências de duas ou mais vogais, digitamos este comando:

grep -E '[aeiou] {2,}' geeks.txt

O comando "grep -E '[aeiou] {2,}' geeks.txt" em uma janela de terminal.

Personagens em fuga

Digamos que desejamos encontrar linhas nas quais um ponto ( .) é o último caractere. Sabemos que o cifrão ( $) é a âncora do fim da linha, então podemos digitar o seguinte:

grep -E '. $' geeks.txt

O comando "grep -E '. $' Geeks.txt" em uma janela de terminal.

No entanto, conforme mostrado abaixo, não recebemos o que esperávamos.

A saída do comando "grep -E '. $' Geeks.txt" em uma janela de terminal.

Conforme abordamos anteriormente, o ponto final ( .) corresponde a qualquer caractere único. Como cada linha termina com um caractere, todas as linhas são retornadas nos resultados.

Então, como você evita que um caractere especial execute sua função regex quando você deseja apenas pesquisar esse caractere real? Para fazer isso, você usa uma barra invertida ( \) para escapar do caractere.

Um dos motivos pelos quais estamos usando as -Eopções (estendidas) é porque elas exigem muito menos escape quando você usa as expressões regulares básicas.

Nós digitamos o seguinte:

grep -e '\. $' geeks.txt

O comando "grep -e '\. $' Geeks.txt" em uma janela de terminal.

Corresponde ao caractere de ponto .final ( ) no final de uma linha.

Ancoragem e Palavras

Abordamos as âncoras de início ( ^) e fim de linha ( $) acima. No entanto, você pode usar outras âncoras para operar nos limites das palavras.

Nesse contexto, uma palavra é uma sequência de caracteres delimitada por espaços em branco (o início ou o fim de uma linha). Portanto, “psy66oh” contaria como uma palavra, embora você não a encontre em um dicionário.

O início da palavra âncora é ( \<); observe que ele aponta para a esquerda, para o início da palavra. Digamos que um nome foi digitado por engano em letras minúsculas. Podemos usar a -iopção grep para realizar uma pesquisa que não diferencia maiúsculas de minúsculas e encontrar nomes que começam com “h”.

Nós digitamos o seguinte:

grep -E -i 'h' geeks.txt

Isso encontra todas as ocorrências de “h”, não apenas aquelas no início das palavras.

grep -E -i '\ <h' geeks.txt

Isso encontra apenas aqueles no início das palavras.

O comando "grep -E -i 'h' geeks.txt" em uma janela de terminal.

Vamos fazer algo semelhante com a letra “y”; queremos apenas ver as instâncias em que está no final de uma palavra. Nós digitamos o seguinte:

grep -E 'y' geeks.txt

Isso encontra todas as ocorrências de “y”, onde quer que apareça nas palavras.

Agora, digitamos o seguinte, usando o final da palavra âncora ( />) (que aponta para a direita, ou o final da palavra):

grep -E 'y \>' geeks.txt

O comando "grep -E 'y' geeks.txt" em uma janela de terminal.

O segundo comando produz o resultado desejado.

Recomendado:  Como fazer com que o Windows apague seu arquivo de página no desligamento (e quando você deve)

Para criar um padrão de pesquisa que procure uma palavra inteira, você pode usar o operador de limite ( \b). Usaremos o operador de limite ( \B) em ambas as extremidades do padrão de pesquisa para encontrar uma sequência de caracteres que deve estar dentro de uma palavra maior:

grep -E '\ bGlenn \ b' geeks.txt
grep -E '\ Bway \ B' geeks.txt

Os comandos "grep -E '\ bGlenn \ b' geeks.txt" e "grep -E '\ Bway \ B' geeks.txt" em uma janela de terminal.

Mais classes de personagens

Você pode usar atalhos para especificar as listas nas classes de caracteres. Esses indicadores de intervalo evitam que você precise digitar todos os membros de uma lista no padrão de pesquisa.

Você pode usar todos os seguintes:

  • AZ: todas as letras maiúsculas de “A” a “Z.”
  • az: todas as letras minúsculas de “a” a “z.”
  • 0-9: Todos os dígitos de zero a nove.
  • dp: todas as letras minúsculas de “d” a “p.” Esses estilos de formato livre permitem que você defina seu próprio intervalo.
  • 2-7: Todos os números de dois a sete.

Você também pode usar quantas classes de caracteres desejar em um padrão de pesquisa. O seguinte padrão de pesquisa corresponde a sequências que começam com “J”, seguido por um “o” ou “s” e, em seguida, um “e”, “h”, “l” ou “s”:

grep -E 'J [os] [ehls]' geeks.txt

O comando "grep -E 'J [os] [ehls]' geeks.txt" em uma janela de terminal.

Em nosso próximo comando, usaremos o a-zespecificador de intervalo.

Nosso comando de pesquisa divide desta forma:

  • H: A sequência deve começar com “H.”
  • [az]: o próximo caractere pode ser qualquer letra minúscula neste intervalo.
  • *:  O asterisco aqui representa qualquer número de letras minúsculas.
  • man: A sequência deve terminar com “man”.

Reunimos tudo no seguinte comando:

grep -E 'H [az] * man' geeks.txt

O comando "grep -E 'H [az] * man' geeks.txt" em uma janela de terminal.

Nada é impenetrável

Algumas regexes podem se tornar rapidamente difíceis de analisar visualmente. Quando as pessoas escrevem regexes complicadas, geralmente começam pequenas e adicionam mais e mais seções até que funcione. Eles tendem a aumentar em sofisticação com o tempo.

Quando você tenta retroceder a partir da versão final para ver o que ela faz, é um desafio totalmente diferente.

Por exemplo, observe este comando:

grep -E '^ ([0-9] {4} [-]) {3} [0-9] {4} | [0-9] {16}' geeks.txt

Por onde você começaria a desembaraçar isso? Vamos começar do início e pegar um pedaço de cada vez:

  • ^: O início da âncora de linha. Portanto, nossa sequência deve ser a primeira coisa em uma linha.
  • ([0-9] {4} [-]): Os parênteses reúnem os elementos do padrão de pesquisa em um grupo. Outras operações podem ser aplicadas a este grupo como um todo (mais sobre isso mais tarde). O primeiro elemento é uma classe de caracteres que contém um intervalo de dígitos de zero a nove [0-9]. Nosso primeiro caractere, então, é um dígito de zero a nove. A seguir, temos uma expressão de intervalo que contém o número quatro {4}. Isso se aplica ao nosso primeiro caractere, que sabemos ser um dígito. Portanto, a primeira parte do padrão de pesquisa agora tem quatro dígitos. Ele pode ser seguido por um espaço ou um hífen ( [- ]) de outra classe de caracteres.
  • {3}:  Um especificador de intervalo contendo o número três segue imediatamente o grupo. É aplicado a todo o grupo, então nosso padrão de pesquisa agora é de quatro dígitos, seguidos por um espaço ou um hífen, que é repetido três vezes.
  • [0-9]: Em seguida, temos outra classe de caracteres que contém uma faixa de dígitos de zero a nove [0-9]. Isso adiciona outro caractere ao padrão de pesquisa e pode ser qualquer dígito de zero a nove.
  • {4}: Outra expressão de intervalo que contém o número quatro é aplicada ao caractere anterior. Isso significa que o caractere se torna quatro caracteres, todos os quais podem ser qualquer dígito de zero a nove.
  • |: O operador de alternância nos diz que tudo à esquerda dele é um padrão de pesquisa completo e tudo à direita é um novo padrão de pesquisa. Portanto, este comando está, na verdade, pesquisando um dos dois padrões de pesquisa. O primeiro é três grupos de quatro dígitos, seguidos por um espaço ou um hífen e, em seguida, outros quatro dígitos adicionados.
  • [0-9]: O segundo padrão de pesquisa começa com qualquer dígito de zero a nove.
  • {16}: Um operador de intervalo é aplicado ao primeiro caractere e o converte em 16 caracteres, todos eles dígitos.

Portanto, nosso padrão de pesquisa procurará um dos seguintes:

  • Quatro grupos de quatro dígitos, com cada grupo separado por um espaço ou um hífen ( -).
  • Um grupo de dezesseis dígitos.

Os resultados são mostrados abaixo.

O comando "grep -E '^ ([0-9] {4} [-]) {3} [0-9] {4} | [0-9] {16}' geeks.txt" em uma janela de terminal .

Este padrão de pesquisa procura formas comuns de escrever números de cartão de crédito. Também é versátil o suficiente para encontrar estilos diferentes, com um único comando.

Vá devagar

A complexidade geralmente é apenas um monte de simplicidade unida. Depois de compreender os blocos de construção fundamentais, você pode criar utilitários eficientes e poderosos e desenvolver novas habilidades valiosas.