Expressões regulares (regex) são uma ferramenta poderosa para busca e manipulação de texto, amplamente utilizadas em scripts shell. Nesta aula, você aprenderá as diferenças entre as expressões regulares básicas (BRE) e estendidas (ERE), e como aplicá-las nos comandos grep, sed e awk, que são pilares do processamento de texto no Linux.

Dominar regex no shell é essencial para automatizar tarefas de filtragem, extração e transformação de dados em arquivos e streams. Vamos explorar desde os conceitos fundamentais até exemplos práticos que você pode usar imediatamente em seus scripts.

Básicas vs estendidas

As expressões regulares podem ser classificadas em dois tipos principais: básicas (BRE) e estendidas (ERE). A diferença está principalmente na sintaxe e nos metacaracteres disponíveis. As BRE são mais antigas e exigem que alguns caracteres sejam escapados com barra invertida para terem significado especial, enquanto as ERE não precisam desse escape adicional.

Por exemplo, na BRE, para usar o quantificador +, você precisa escrever \+. Já na ERE, basta usar +. Da mesma forma, os parênteses de agrupamento em BRE são \( e \), enquanto em ERE são ( e ).

Os comandos grep, sed e awk têm opções para selecionar o tipo de expressão regular. Por exemplo, no grep você pode usar -E para ERE e -G (ou nenhuma opção) para BRE. No sed, a opção -E ou -r ativa ERE, enquanto no awk as expressões são sempre ERE (na maioria das implementações).

Em grep, sed, awk

Cada comando tem sua própria forma de aplicar regex. O grep filtra linhas que correspondem a um padrão, o sed é um editor de fluxo que pode substituir ou modificar texto, e o awk é uma linguagem de programação orientada a texto que processa linhas e campos. Todos eles suportam regex, mas com nuances.

grep: usa regex para encontrar linhas correspondentes. Você pode usar opções como -E (ERE), -i (ignorar maiúsculas), -v (inverter a seleção), entre outras.

# Exemplo com grep -E (ERE)
grep -E "^[0-9]{3}-[0-9]{4}$" telefones.txt

sed: usa regex principalmente em comandos de substituição (s/.../.../) e endereçamento de linhas. Por padrão, usa BRE, mas com -E você ativa ERE.

# Substituir qualquer número de 4 dígitos por "####" usando ERE
sed -E 's/[0-9]{4}/####/g' dados.txt

awk: a sintaxe de regex é semelhante ao ERE, e você pode usar em padrões de linha ou em funções de string. O awk é mais poderoso para processamento de campos.

# Imprimir linhas onde o campo 2 contém "erro" (case insensitive)
awk -F: '$2 ~ /erro/i' arquivo.txt

Diferenças

As principais diferenças entre BRE e ERE afetam a escrita do padrão. Vamos listar os caracteres que têm significado especial e como são representados em cada tipo:

  • Quantificadores: * (zero ou mais) é igual em ambos; + (um ou mais) e ? (zero ou um) são ERE, em BRE precisam ser escapados: \+ e \?.
  • Alternância: | (ou) é ERE, em BRE é \|.
  • Agrupamento: parênteses (...) em ERE, em BRE são \(...\).
  • Chaves: {m,n} (intervalo) é ERE, em BRE é \{m,n\}.

Outra diferença importante é o suporte a classes de caracteres POSIX, como [:alpha:], [:digit:], que funcionam em ambos, mas é bom lembrar que dentro de colchetes.

No grep, por padrão, ele usa BRE, mas com -E você obtém ERE. Algumas versões têm -P para Perl regex, que é ainda mais avançada, mas não é padrão em todos os sistemas.

Exemplos

Vamos ver exemplos práticos que combinam regex com grep, sed e awk.

Exemplo 1: Validar endereços de e-mail (simplificado)

# Usando grep -E para encontrar e-mails
grep -E "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$" emails.txt

Exemplo 2: Extrair IPs de um arquivo de log

# Com grep -o para extrair apenas os IPs
grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" access.log | sort -u

Exemplo 3: Substituir múltiplos espaços por um único

# Usando sed -E
sed -E 's/[[:space:]]+/ /g' texto.txt

Exemplo 4: Somar valores de um campo com awk

# Somar valores numéricos no campo 3 (separados por vírgula)
awk -F, '$3 ~ /^[0-9]+$/ {soma += $3} END {print "Total:", soma}' dados.csv

Exemplo 5: Usar backreferences em sed

# Trocar ordem de nome e sobrenome (ex.: "João Silva" -> "Silva, João")
echo "João Silva" | sed -E 's/([A-Za-z]+) ([A-Za-z]+)/\2, \1/'

Boas práticas e observações

Quando estiver escrevendo scripts, prefira usar ERE (-E) para maior clareza, a menos que precise de compatibilidade máxima com sistemas antigos. Sempre teste suas regex em um arquivo pequeno antes de aplicar em dados reais. Use aspas simples para proteger o padrão da expansão do shell. Lembre-se de escapar caracteres como \ quando necessário.

Além disso, esteja ciente de que o awk tem sua própria sintaxe de regex, mas é compatível com a maioria dos recursos ERE. Para tarefas complexas, considere usar perl ou python que têm suporte a regex mais poderoso, mas isso foge ao escopo desta aula.

Exercícios

  1. Escreva um comando grep para encontrar linhas em um arquivo que contenham a palavra "erro" ou "falha", ignorando maiúsculas/minúsculas, usando ERE.

    ✓ Resposta:
    grep -E -i "erro|falha" arquivo.txt
  2. Use sed para substituir todas as ocorrências de "foo" por "bar" em um arquivo, mas apenas quando "foo" estiver no início da linha.

    ✓ Resposta:
    sed -E 's/^foo/bar/' arquivo.txt
  3. Com awk, imprima apenas as linhas em que o segundo campo (separado por espaço) contém um número de telefone no formato (XX) XXXXX-XXXX.

    ✓ Resposta:
    awk '$2 ~ /^\([0-9]{2}\) [0-9]{5}-[0-9]{4}$/' arquivo.txt
  4. Explique a diferença entre BRE e ERE em relação aos parênteses e à alternância, e dê um exemplo de uso em grep.

    ✓ Resposta: Em BRE, parênteses e barra vertical devem ser escapados: \(, \), \|. Em ERE, usamos diretamente (, ), |. Exemplo: para buscar "gato" ou "cachorro", em BRE: grep 'gato\|cachorro'; em ERE: grep -E 'gato|cachorro'.
  5. Crie um comando que use awk para extrair a soma de todos os números contidos em uma coluna de um arquivo CSV, onde os números podem ter casas decimais.

    ✓ Resposta:
    awk -F, '$3 ~ /^[0-9]+(\.[0-9]+)?$/ {soma += $3} END {printf "%.2f\n", soma}' arquivo.csv

Referências