Linux / Comando awk: extrair campos, filtrar e somar valores

awk separa texto em registros e campos para extrair colunas, filtrar linhas e calcular totais. Por exemplo, awk '{print $2}' arquivo mostra o segundo campo de cada registro. No modo padrão, cada linha é um registro, e sequências de espaços ou tabulações separam os campos.

Um registro é a unidade de entrada processada de cada vez; um campo é uma parte desse registro. Use $0 para o registro inteiro, $1 para o primeiro campo e $2 para o segundo. NF informa quantos campos há no registro atual, e NR conta os registros lidos desde o início da execução.

Sintaxe, regras e implementação

awk [opção...] 'condição { ação }' [arquivo...]

A linha acima descreve a sintaxe e contém marcadores. Uma regra combina uma condição e uma ação entre chaves; quando a condição é verdadeira, awk executa a ação. Sem condição, a ação vale para todos os registros. Mantenha programas fixos entre aspas simples para que o shell não expanda $1 e outros trechos antes de awk recebê-los.

Confira a implementação instalada com awk --version, se ela oferecer essa opção, ou consulte man awk. Os exemplos foram conferidos com GNU Awk 5.4.0 no Bash do Git para Windows, em arquivos locais com finais de linha LF. Recursos específicos de gawk e detalhes do ambiente precisam ser verificados ao usar outras implementações.

Preparar dados e comparar campos, filtros e soma

Execute o bloco inteiro no Bash, dentro de uma pasta onde possa criar arquivos. Ele prepara uma pasta exclusiva e trabalha em um subshell entre parênteses. set -eu interrompe este bloco simples diante de falhas não tratadas ou variáveis não definidas. A cópia valid.txt conserva os três registros válidos para os exemplos seguintes, enquanto amounts.txt receberá um erro proposital.

(
set -eu
labdir=$(mktemp -d ./awk-demo.XXXXXX)
cd "$labdir"
printf 'name amount\nAna 12\nBob 7\nCara 11\n' > amounts.txt
cp amounts.txt valid.txt
awk 'NR > 1 {print $2}' amounts.txt
awk 'NR > 1 && $2 >= 10 {print $1}' amounts.txt
awk 'NR > 1 {sum += $2} END {print sum}' amounts.txt
printf 'Dana missing\n' >> amounts.txt
awk 'NR == 1 {next}
     NF != 2 || $2 !~ /^[0-9]+([.][0-9]+)?$/ {
         print "linha inválida " NR ": " $0 > "/dev/stderr"
         bad = 1; next
     }
     {sum += $2}
     END {if (bad) exit 1; print sum}' amounts.txt || test "$?" -eq 1
printf 'Arquivos do laboratório: %s\n' "$PWD"
)

As primeiras saídas são 12, 7 e 11, uma por linha; depois aparecem Ana e Cara, seguidas da soma 30. A condição NR > 1 ignora o primeiro registro, que contém o cabeçalho. && exige que as duas condições sejam verdadeiras, e sum += $2 acumula o valor do segundo campo.

Ao acrescentar Dana missing, o bloco de validação escreve linha inválida 5: Dana missing na saída de erro padrão. Ele termina com status 1 e não imprime um total de sucesso. Como a falha é proposital, || test "$?" -eq 1 permite que o bloco continue até mostrar a pasta de prática. Essa expressão aceita status 1; para uma rotina real, distinga a rejeição dos dados de outros erros e examine também o diagnóstico.

A última linha mostra o caminho completo da pasta criada. Entre nele com cd e aspas para continuar os exemplos. Ao sair do subshell, a sessão volta ao diretório anterior. valid.txt mantém o cabeçalho e as três linhas originais; amounts.txt também contém a linha inválida. Use valid.txt nas consultas que pressupõem valores numéricos já conferidos.

Entender campos e separadores

No modo padrão, espaços consecutivos e tabulações funcionam como um separador, e os espaços nas extremidades são ignorados ao dividir os campos. printf ' Ana 12\t\n' | awk '{print NF, $1, $2}' produz 2 Ana 12. A contagem NF descreve os campos, não a quantidade de espaços entre eles.

O conteúdo de $0 continua sendo o registro completo enquanto você apenas lê seus campos. Já print $1, $2 imprime dois valores separados por OFS, o separador de saída, que por padrão é um espaço. Atribuir um novo valor a um campo pode reconstruir $0 com OFS; isso pode mudar o espaçamento original.

Para dados separados por dois-pontos, printf 'Ana::12\n' | awk -F: '{print NF, "[" $2 "]", $3}' produz 3 [] 12. Os dois delimitadores consecutivos criam um segundo campo vazio. Esse comportamento difere do agrupamento de espaços do modo padrão. Escolha -F conforme o formato real, e confira NF antes de acessar campos obrigatórios.

Para escolher o separador da saída, awk -v OFS=';' 'NR > 1 {print $1, $2}' valid.txt mostra Ana;12, Bob;7 e Cara;11. Mudar OFS altera a saída de print com vírgulas; não muda o separador usado para ler a entrada.

Passar valores e escolher registros

Use -v para fornecer um valor inicial a uma variável de awk. awk -v limite=10 'NR > 1 && $2 >= limite {print $1, $2}' valid.txt produz Ana 12 e Cara 11. Alterar limite para 12 mantém apenas Ana 12. A variável evita inserir o valor diretamente dentro do texto do programa; valide também os parâmetros recebidos de outras fontes.

O teste acima pressupõe o arquivo válido da prática. Uma comparação entre um campo e um número não valida o formato do campo. Textos e valores numéricos seguem regras de conversão e comparação próprias; confira os dados antes de filtrar ou somar. Forçar uma conversão com +0 também não comprova que a entrada era um número válido.

NR é cumulativo quando uma execução lê vários arquivos. FNR conta os registros do arquivo atual. Compare com awk 'FNR == 1 {print FILENAME, NR, FNR}' valid.txt valid.txt: a saída é valid.txt 1 1 e valid.txt 5 1. Para pular um cabeçalho em cada arquivo, avalie FNR; NR > 1 pula apenas o primeiro registro de toda a entrada.

Usar BEGIN e END sem aceitar dados inválidos

BEGIN executa antes da leitura dos registros, e END executa ao final. Para a entrada conhecida da prática, awk 'BEGIN {print "INICIO"} NR > 1 {sum += $2} END {print "TOTAL", sum}' valid.txt começa com INICIO e termina com TOTAL 30. O total deve ser emitido depois de processar os registros, e não uma vez para cada linha.

No validador do bloco completo, next passa ao próximo registro, e bad = 1 registra que houve um erro. O trecho END consulta essa marca antes de imprimir a soma. Se uma linha falhar, o programa não apresenta a soma parcial como um resultado completo.

A expressão ^[0-9]+([.][0-9]+)?$ aceita dígitos e uma parte decimal opcional com ponto, como 0, 12 ou 12.5. Ela rejeita campos vazios, missing, 12x, números negativos, expoentes e vírgula decimal. Essa regra atende aos dados da prática; defina outra regra se o seu formato aceitar outros números. Validar a forma escrita também não elimina limites de precisão ou tamanho da aritmética usada.

O bloco presume que o primeiro registro é um cabeçalho e não confere seus nomes. Ele também não exige uma quantidade mínima de registros válidos. Se o relatório depender dessas condições, valide o cabeçalho e a presença de dados antes de aceitar o resultado. Não trate um arquivo vazio, valores ausentes ou uma falha de leitura como uma soma de dados completos.

Distinguir separador simples de CSV

awk -F, apenas divide os campos por vírgulas no modo comum; isso não interpreta as regras de CSV, sigla em inglês de valores separados por vírgulas. Por exemplo, printf '"Ana, Silva",12\n' | awk -F, '{print NF}' informa 3 campos, embora o nome entre aspas deva formar um único campo.

O GNU gawk oferece --csv desde a versão 5.3. No gawk 5.4.0 conferido, printf '"Ana, Silva",12\n' | gawk --csv '{print NF, $1, $2}' produz 2 Ana, Silva 12, respeitando a vírgula dentro do nome. Esse modo também trata registros com quebras de linha entre aspas e substitui a divisão comum por FS e RS. Confira a documentação do modo CSV do gawk e sua disponibilidade na implementação instalada.

Para outras versões e formatos, use um analisador adequado ao CSV recebido. O modo CSV do gawk não é um seletor de qualquer delimitador, como ponto e vírgula. Além disso, imprimir campos separados por vírgulas não cria automaticamente o escape necessário para escrever um novo CSV válido.

Salvar a saída e verificar falhas

Awk normalmente envia o resultado à saída padrão e preserva os arquivos de entrada. Para salvar, escolha um arquivo de saída separado e ainda não usado, revise o conteúdo e compare com a entrada. Não use o mesmo caminho como entrada e destino de >: o shell pode esvaziar o arquivo antes que awk leia os dados.

Confira mensagens na saída de erro e o status imediatamente após o comando com echo "$?", quando ele for executado isoladamente. Uma consulta que não seleciona registros pode terminar com sucesso e saída vazia. Em um pipeline, o status do último comando pode esconder falhas anteriores; examine os comandos envolvidos ou as opções de tratamento de falhas do shell utilizado.

Opções e elementos principais

Elemento Uso
-F separador Define como dividir os campos de entrada no modo comum.
-v nome=valor Inicializa uma variável antes da execução, inclusive antes de BEGIN.
BEGIN Executa uma ação antes de ler os registros de entrada.
END Executa uma ação ao final; confira erros antes de emitir um total.

Pergunta frequente

Qual é a diferença entre $0 e $1 no awk?

$0 representa o registro atual inteiro; $1 representa o primeiro campo, conforme o separador da entrada. NF conta os campos. No modo padrão, imprimir $0 preserva o texto lido se ele não tiver sido reconstruído por alterações nos campos, enquanto print $1, $2 usa o separador de saída OFS.

Documentação

Consulte o manual oficial do GNU Awk para regras de campos, tipos e leitura de registros. A página de manual do gawk publicada pelo Debian reúne as opções e os elementos da linguagem.