Expressões regulares no shell
Esta aula explora as expressões regulares no shell Linux/Bash, cobrindo as diferenças entre BRE e ERE, como usá-las em grep, sed e awk, e fornecendo exemplos práticos para cada ferramenta. Você aprenderá a aplicar regex de forma eficaz em scripts e comandos do dia a dia.
Expressões regulares (regex) são padrões de busca utilizados para localizar, extrair e manipular texto de forma poderosa. No contexto do shell Linux/Bash, elas são essenciais para processamento de texto em arquivos, logs, saídas de comandos e automação de tarefas. Nesta aula, vamos mergulhar nas duas principais variações de regex usadas no shell: as básicas (BRE) e as estendidas (ERE), e como elas se aplicam aos utilitários clássicos grep, sed e awk.
Dominar regex no shell é um divisor de águas: o que antes exigia múltiplos comandos ou scripts complexos pode ser resolvido com uma única expressão bem elaborada. Ao final desta aula, você terá compreensão sólida das diferenças entre BRE e ERE, saberá quando cada uma é necessária e verá exemplos práticos que poderá aplicar imediatamente.
Básicas vs estendidas
As expressões regulares no Linux seguem dois padrões principais: BRE (Basic Regular Expressions) e ERE (Extended Regular Expressions). A diferença fundamental está na sintaxe dos metacaracteres: em BRE, certos caracteres como ?, +, {, }, (, ) e | têm significado literal, a menos que sejam escapados com uma barra invertida (ex.: \?). Em ERE, esses mesmos caracteres têm significado especial sem necessidade de escape.
Na prática, isso significa que em BRE, para usar repetição opcional (?), uma ou mais vezes (+), ou alternância (|), você precisa escrever \?, \+, \|. Já em ERE, escreve-se simplesmente ?, + e |. Por exemplo, a expressão colou?r em ERE corresponde a "color" ou "colour"; em BRE, deve ser escrita como colou\?r. Essa diferença pode causar confusão, mas lembrar que ERE é mais intuitiva ajuda.
Outra diferença é o suporte a intervalos: {n,m} (quantificador) é suportado em ERE sem escape, e em BRE com escape (\{n,m\}). Grupos de captura também seguem o mesmo princípio: em ERE, (abc) agrupa; em BRE, \(abc\).
A maioria das ferramentas modernas do Linux, como grep com a opção -E, sed com -E, e awk por padrão, utilizam ERE. No entanto, é crucial conhecer BRE, pois muitos scripts antigos e algumas ferramentas (como o grep sem opções) ainda a usam por padrão.
Para ilustrar, vejamos um exemplo simples: procurar linhas que contenham "error" ou "warning" em um arquivo de log.
# Usando BRE (grep padrão) - é necessário escapar o pipe
$ grep 'error\|warning' /var/log/syslog
# Usando ERE (grep -E) - sem escape
$ grep -E 'error|warning' /var/log/syslogAmbos produzem o mesmo resultado, mas a sintaxe é diferente. A partir de agora, quando você vir comandos com \|, saiba que é BRE; com |, é ERE.
Em grep, sed, awk
As três ferramentas mais comuns para processamento de texto no shell são grep, sed e awk. Cada uma tem suas particularidades no suporte a regex.
grep é o filtro por excelência: ele lê linhas de entrada e imprime aquelas que correspondem a um padrão. Por padrão, usa BRE; com -E, usa ERE; com -P, usa PCRE (Perl Compatible Regular Expressions), que é ainda mais poderoso e similar ao que você encontra em linguagens como Python. O grep é ideal para buscas simples e rápidas, e é frequentemente usado em pipelines para filtrar a saída de outros comandos.
sed (stream editor) é mais que um filtro: ele pode substituir, deletar, inserir e transformar texto. Sua sintaxe de substituição é s/pattern/replacement/flags. Por padrão, também usa BRE, mas com a opção -E (ou -r em algumas versões) você ativa ERE. O sed é perfeito para operações de edição em arquivos de configuração, relatórios e qualquer transformação textual.
awk é uma linguagem de programação orientada a linhas e campos. Ele usa ERE por padrão em suas expressões de correspondência (~ e !~). O awk é extremamente poderoso para processamento de dados estruturados, como arquivos CSV ou saídas de comandos com colunas, permitindo operações complexas com condicionais e loops.
Vamos ver como cada ferramenta lida com a mesma tarefa: extrair endereços de e-mail de um texto.
# Com grep -E (ERE)
$ grep -E '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b' arquivo.txt
# Com sed -E (substitui por "[EMAIL]") e imprime somente linhas com e-mail
$ sed -E 's/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/[EMAIL]/' arquivo.txt | grep '[EMAIL]'
# Com awk (imprime o primeiro campo que contém @)
$ awk '{for(i=1;i<=NF;i++) if($i ~ /@/) print $i}' arquivo.txtPerceba que a regex é a mesma, mas a forma de aplicá-la varia. O grep apenas filtra; o sed pode transformar; o awk pode fazer análises mais complexas, como percorrer campos.
Diferenças
Além da sintaxe BRE vs ERE, há diferenças importantes no comportamento e nos recursos entre essas ferramentas, mesmo quando usam ERE.
- Escopo de atuação:
grepopera em linhas completas;sedopera em linhas, mas pode modificar o conteúdo;awkdivide cada linha em campos, permitindo operações por campo. - Captura de grupos: Todas suportam grupos de captura, mas a forma de referenciá-los difere. No
sed, usa-se\1,\2etc. no padrão de substituição. Noawk, usa-sematch()esub()ougsub()comRSTARTeRLENGTH, ou ainda com a funçãogensub()em algumas versões. Nogrep, não há substituição, apenas correspondência. - Opções de linha de comando:
grepoferece-opara imprimir apenas a parte correspondente,-ipara ignorar maiúsculas,-vpara inverter a seleção.sedtem opções como-npara suprimir a saída padrão e-ipara editar arquivo in-place.awktem variáveis internas comoFS(separador de campos) eOFS(separador de saída), além de funções comosub()egsub(). - Performance: Para buscas simples em arquivos grandes,
grepé geralmente o mais rápido.awké mais pesado, mas mais flexível.sedé eficiente para edições.
Um exemplo de diferença prática: se você quiser extrair o segundo campo de linhas que começam com "ID:", o awk é muito mais direto que sed ou grep.
# Arquivo: dados.txt
# ID: 123 Nome: João
# ID: 456 Nome: Maria
# awk - imprime o segundo campo (índice 2) das linhas que começam com ID:
$ awk '/^ID:/ {print $2}' dados.txt
123
456
# sed - para obter o mesmo, precisamos capturar em grupo e substituir
$ sed -E 's/^ID: ([0-9]+).*/\1/' dados.txt
123
456
# grep - apenas extrai a parte correspondente (não é ideal para campos)
$ grep -oE '^ID: [0-9]+' dados.txt | grep -oE '[0-9]+$'Como você pode ver, cada ferramenta tem seu ponto forte. Saber escolher entre elas é uma habilidade importante.
Exemplos
Vamos consolidar com exemplos práticos que combinam regex e as três ferramentas em situações comuns.
Exemplo 1: Extrair IPs de um log
# Log de acesso: linhas como "192.168.1.1 - - [10/Oct/2023:13:55:36] GET /index.html 200"
$ grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log | sort | uniq -c | sort -nr
# Explicação: grep -oE extrai apenas a correspondência, que é o IP. Em seguida, contamos ocorrências.Exemplo 2: Substituir datas no formato DD/MM/AAAA para AAAA-MM-DD
# Arquivo: relatorio.txt
# Data: 25/12/2023
$ sed -E 's#([0-9]{2})/([0-9]{2})/([0-9]{4})#\3-\2-\1#g' relatorio.txt
Data: 2023-12-25
# Usamos # como delimitador para evitar escapar as barras.Exemplo 3: Somar valores de uma coluna com awk
# Arquivo: vendas.txt
# Produto,Quantidade,Preço
# Caneta,10,2.50
# Caderno,5,15.00
$ awk -F',' 'NR>1 {total += $2 * $3} END {printf "Total: R$ %.2f\n", total}' vendas.txt
Total: R$ 100.00
# Aqui usamos regex implícita no separador de campos (FS) e expressões aritméticas.Exemplo 4: Validar formato de e-mail com grep
# Para listar apenas e-mails válidos em um arquivo:
$ grep -E '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$' contatos.txtExemplo 5: Extrair títulos de tags HTML com sed
# Arquivo: pagina.html
# Bem-vindo
Texto
$ sed -E 's/([^<]+)<\/h1>/Título: \1/' pagina.html
Título: Bem-vindo
# [^<]+ corresponde a qualquer caractere que não seja '<', capturando o conteúdo.
Esses exemplos mostram o poder da combinação de regex com as ferramentas do shell. Pratique adaptando-os aos seus dados.
Boas práticas e observações finais
Ao trabalhar com regex no shell, algumas boas práticas ajudam a evitar erros e tornar seus scripts mais legíveis:
- Use aspas simples ao redor de expressões regulares para evitar que o shell expanda metacaracteres como
$,*ou!. - Prefira ERE (
-E) em scripts novos, pois é mais clara e menos sujeita a erros de escape. - Teste suas expressões com arquivos pequenos ou usando
echoantes de aplicar em dados reais. - Documente expressões complexas com comentários, pois regex pode se tornar ilegível rapidamente.
- Considere a performance: para arquivos enormes,
grepé mais rápido queawk; useawkapenas quando precisar de processamento por campos.
Com essas ferramentas e conceitos, você está pronto para manipular texto de forma profissional no shell. Lembre-se de que a prática é fundamental: crie seus próprios desafios e resolva-os usando regex.
Referências
- GNU Grep Manual
- GNU Sed Manual
- GNU Awk User's Guide
- Regular-Expressions.info: POSIX Bracket Expressions
- POSIX Regular Expressions (Base Definitions)
- Advanced Bash-Scripting Guide: Regular Expressions
Exercícios
- Exercício 1: Usando
grep -E, escreva um comando para listar todas as linhas de um arquivodados.txtque contenham um número de telefone no formato(XX) XXXXX-XXXX, onde X é dígito. - Exercício 2: Com
sed -E, substitua emtexto.txttodas as ocorrências de "foo" por "bar", mas apenas quando "foo" for uma palavra inteira (não parte de "foobar"). - Exercício 3: Usando
awk, imprima a quantidade de linhas deregistro.logque contêm a palavra "ERROR" (case-sensitive). - Exercício 4: Extraia todos os endereços IP (IPv4) de um arquivo
ips.txtusandogrep -oE, mas apenas IPs da faixa 192.168.x.x. - Exercício 5: Com
sed -E, transforme linhas no formato "Nome Sobrenome" para "Sobrenome, Nome" em um arquivonomes.txt.
grep -E '\([0-9]{2}\) [0-9]{5}-[0-9]{4}' dados.txtsed -E 's/\bfoo\b/bar/g' texto.txtawk '/ERROR/ {count++} END {print count}' registro.loggrep -oE '192\.168\.[0-9]{1,3}\.[0-9]{1,3}' ips.txtsed -E 's/^([A-Za-z]+) ([A-Za-z]+)$/\2, \1/' nomes.txt