Comando awk: processar texto campo a campo no Linux

Ilustração do comando awk do Linux a decompor um registo de texto em campos numerados

Quem administra sistemas ou trabalha em cibersegurança passa boa parte do dia a olhar para texto estruturado: registos de acesso de um servidor web, o /etc/passwd, ficheiros CSV de inventário, saídas de comandos com colunas alinhadas. O awk nasceu precisamente para isso: lê a entrada registo a registo, parte cada registo em campos e aplica regras simples do tipo «se este padrão for verdadeiro, executa esta ação».

O resultado é uma ferramenta que, numa única linha de terminal, resolve tarefas que noutras linguagens exigiriam um script completo: extrair a coluna certa, filtrar por condição, somar valores, contar ocorrências e formatar um relatório. Neste artigo percorremos a estrutura do awk, as opções e variáveis que realmente interessam, e terminamos com um simulador interativo onde pode ver a decomposição em campos a acontecer, linha a linha.

O que faz o awk

O awk é, na verdade, uma pequena linguagem de programação orientada a registos. Um programa awk é uma lista de regras, cada uma com a forma padrão { ação }. Para cada registo lido (por omissão, cada linha), o awk avalia o padrão; se for verdadeiro, executa a ação. Antes disso, parte o registo em campos usando o separador definido em FS — por omissão, sequências de espaços e tabulações.

awk [opções] 'padrão { ação }' ficheiro...

Ambas as partes da regra são opcionais: sem padrão, a ação corre para todos os registos; sem ação, assume-se {print}, que imprime o registo inteiro. Em distribuições Linux, o comando awk costuma ser fornecido pelo gawk (GNU awk) ou pelo mawk, ambos compatíveis com o núcleo da linguagem definido no POSIX.

As opções que interessam

Campos e variáveis internas

  • $0 — o registo completo; $1 a $NF — os campos individuais, sendo $NF sempre o último, seja qual for o número de colunas.
  • NF — número de campos do registo atual; NR — número do registo desde o início; FNR — número do registo dentro do ficheiro atual (útil com vários ficheiros).
  • FS e OFS — separadores de campos de entrada e de saída; RS e ORS — separadores de registos; FILENAME — nome do ficheiro em leitura.
awk '{print $1, $7}' access.log     # IP e código de estado
awk '{print NR": "$0}' relatorio.txt  # numera as linhas

Um pormenor que faz toda a diferença: no print, a vírgula entre argumentos insere o OFS (um espaço, por omissão); sem vírgula, o awk concatena os valores sem separador nenhum. print $1, $7 e print $1 $7 produzem resultados muito diferentes.

Padrões: regex, comparações e intervalos

O padrão pode ser uma expressão regular entre barras, aplicada a $0, ou qualquer condição sobre campos e variáveis. O operador ~ testa uma regex contra um campo específico (!~ nega), e &&, || e ! combinam condições. Um par padrão1, padrão2 define um intervalo: imprime desde o primeiro registo que casa com o primeiro padrão até ao que casa com o segundo.

awk '/403|500/ {print $1}' access.log        # regex sobre $0
awk '$7 == 404 {print $6}' access.log        # comparação num campo
awk -F':' '$3 >= 1000 {print $1}' /etc/passwd  # condição numérica
awk 'NR==2, NR==4 {print}' dados.txt         # intervalo de registos

BEGIN, END e acumuladores

O bloco BEGIN corre uma vez antes de ler o primeiro registo — ideal para imprimir cabeçalhos ou definir variáveis como o FS. O bloco END corre uma vez depois do último registo, quando NR já vale o total de registos lidos. Como as variáveis não precisam de ser declaradas e começam a zero (ou cadeia vazia), somar e contar torna-se trivial.

awk '{s += $10} END {print s}' access.log   # soma de bytes transferidos
awk 'END {print NR}' access.log             # equivalente a wc -l

Opções da linha de comandos

  • -F sep — define o separador de campos, por exemplo -F':' para o /etc/passwd ou -F',' para CSV simples. Aceita também uma expressão regular.
  • -v var=valor — passa uma variável da shell para dentro do programa, de forma segura e sem truques de aspas.
  • -f ficheiro.awk — lê o programa de um ficheiro, o formato certo quando a regra deixa de caber confortavelmente numa linha.
awk -F',' -v limite=100 '$3 > limite {print $2}' vendas.csv

Ações, funções e arrays associativos

Além do print, o printf dá controlo total sobre o formato (%s, %d, %8.2f, %-10s). As funções de texto incluem length(), substr(), index(), split(), sub()/gsub() para substituições com regex, e toupper()/tolower(). As instruções next e exit saltam para o registo seguinte ou terminam o programa. A joia da coroa são os arrays associativos, indexados por texto — a base do idioma clássico de contagem:

awk '{c[$1]++} END {for (ip in c) print c[ip], ip}' access.log
awk -F',' 'NR>1 {t[$1] += $3*$4} END {for (r in t) printf "%-8s %8.2f\n", r, t[r]}' vendas.csv

awk em cibersegurança

Em segurança, o awk é uma das ferramentas de triagem mais rápidas que existem, porque já está instalado em praticamente qualquer sistema:

  • Análise de logs web: contar pedidos por IP ({c[$1]++}), isolar códigos 4xx/5xx ou detetar tentativas de ataque como pedidos a /etc/passwd ou a páginas de autenticação — o primeiro passo de qualquer investigação de incidente.
  • Auditoria de contas: awk -F':' '$3 == 0 {print $1}' /etc/passwd revela contas com UID 0 além do root; $7 ~ /bash/ lista quem tem shell interativa — perguntas obrigatórias em hardening.
  • Deteção de exfiltração: somar bytes transferidos por origem ({b[$1] += $10} END {...}) evidencia num instante o cliente que descarregou dados muito acima do normal.
  • Forense e relatórios: extrair e realinhar colunas de saídas de comandos de rede e processos, transformando texto em bruto em evidência legível para o relatório.

Experimente no simulador interativo

O simulador abaixo mostra o motor do awk por dentro: para cada registo, vê o $0, a decomposição em campos $1$NF, os valores de NR, NF e FS, e se o padrão foi verdadeiro ou falso. Escolha um dos programas na grelha (de {print $1} a arrays associativos com END), use Anterior/Seguinte para navegar ou Demonstrar tudo para ver a sequência completa sobre três ficheiros de exemplo: um log de acessos, um passwd e um CSV de vendas.

Erros comuns e boas práticas

  • Confundir print $1, $7 (com OFS) e print $1 $7 (concatenação sem separador) — o clássico de quem está a começar.
  • Esquecer as plicas à volta do programa: sem elas, a shell expande $1 antes de o awk o ver. Use sempre awk '...' e passe valores externos com -v.
  • Tratar CSV real com -F',': campos com vírgulas dentro de aspas quebram a divisão. Para CSV simples serve; para CSV complexo, use uma ferramenta com parser dedicado.
  • Contar colunas à mão quando $NF (último campo) e NF resolvem o problema de forma robusta, mesmo com número de colunas variável.
  • Ignorar que variáveis não declaradas valem 0 ou cadeia vazia: é conveniente, mas também esconde erros de escrita no nome da variável.
  • Usar extensões específicas do gawk (como gensub()) em scripts que vão correr em sistemas com mawk ou awk POSIX — teste a portabilidade antes de distribuir.
  • Saltar o cabeçalho de ficheiros tabulares com NR > 1 — pequeno hábito que evita somas e contagens contaminadas pela linha de títulos.

Conclusão

O awk condensa num só comando um ciclo de leitura, um parser de campos e uma linguagem de regras. Dominar meia dúzia de idiomas — {print $n}, padrões com regex e comparações, -F para mudar o separador, BEGIN/END e arrays associativos — chega para resolver a maioria das tarefas de análise de texto em administração de sistemas e segurança. Combine-o com o histórico da série, como a gestão de contas com o useradd, e o terminal passa a ser a sua primeira ferramenta de auditoria.

Se quer levar estas competências mais longe — da linha de comandos à resposta a incidentes — conheça os serviços de formação e consultoria em cibersegurança da HJFR no nosso catálogo de serviços.

Este artigo faz parte da série “Comandos Linux, um a um” do blog HJFR — fique atento aos próximos comandos.