Ubuntu awk

O awk enxerga cada linha de texto como uma linha de planilha, com colunas. Com ele você pega a terceira coluna, filtra as linhas em que um valor passa de 1000, soma uma coluna inteira ou monta um relatório. O nome vem das iniciais dos seus criadores: Aho, Weinberger e Kernighan.

Você vai aprender
  • O que são registros e campos, e como o awk divide cada linha.
  • A estrutura padrão { ação }, explicada parte por parte.
  • A imprimir colunas, filtrar linhas, somar, tirar médias e formatar.
  • Por que, no Ubuntu em português, o awk pode somar 1200.50 como 1200, e como corrigir.

No Ubuntu 26.04, o awk padrão é o mawk, uma versão rápida e enxuta. As sessões abaixo foram feitas com ele. O simulador do guia não tem o awk; pratique no seu terminal.


Registros e campos

Vamos usar este arquivo de vendas: nome, região e valor.

$ cat vendas.txt
ana    norte  1200.50
bruno  sul    800
carla  norte  450.25
davi   leste  2100

Para o awk, cada linha é um registro. Cada pedaço separado por espaços é um campo, numerado a partir de 1. Vários espaços seguidos contam como um separador só.

VariávelNa linha "ana norte 1200.50"Significado
$1anaPrimeiro campo
$2norteSegundo campo
$31200.50Terceiro campo
$0ana norte 1200.50A linha inteira
NF3Number of Fields: quantos campos a linha tem
$NF1200.50O último campo
NR1Number of Record: o número da linha

Imprimir uma coluna

awk
O programa.
'{
Abre a ação. As aspas simples protegem o $1 do shell.
print $1
A ação: imprima o campo 1.
}'
Fecha a ação.
vendas.txt
O arquivo. Sem ele, o awk lê a entrada (um pipe, por exemplo).
$ awk '{print $1}' vendas.txt
ana
bruno
carla
davi

A ação entre chaves roda para cada linha. Por isso saíram quatro nomes.

Várias colunas e texto junto

Separe os campos com vírgula para sair um espaço entre eles. Texto fixo vai entre aspas duplas:

$ awk '{print $1, $3}' vendas.txt
ana 1200.50
bruno 800
carla 450.25
davi 2100
$ awk '{print NR ": " $1}' vendas.txt
1: ana
2: bruno
3: carla
4: davi
$ awk '{print NF, $NF}' vendas.txt
3 1200.50              « 3 campos; o último é o valor
3 800
3 450.25
3 2100

Por que não usar cut?

O cut separa por um caractere exato. Com vários espaços seguidos, ele se perde. O awk não:

$ cut -d " " -f 3 vendas.txt
                       « vazio: entre "ana" e "norte" há vários espaços
sul
$ awk '{print $3}' vendas.txt
1200.50
800
450.25
2100
FerramentaMelhor quando…
cut -d ";" -f 2O separador é um caractere fixo e único (CSV, /etc/passwd).
awk '{print $2}'As colunas são separadas por espaços variáveis (ls -l, ps, df).
awk com condiçõesVocê também quer filtrar ou calcular.

Mudar o separador: -F

O /etc/passwd separa os campos com :. A opção -F (de field separator) diz isso ao awk:

awk
O programa.
-F:
O separador de campos é :.
'{print $1}'
Imprime o campo 1: o nome do usuário.
/etc/passwd
A lista de contas do sistema.
$ awk -F: '{print $1}' /etc/passwd | head -n 3
root
daemon
bin

Para um CSV, use -F, ou -F';'. Para mudar o separador da saída, defina a variável OFS: awk -v OFS=';' '{print $1, $3}' vendas.txt.


Filtrar linhas: o padrão

Um programa awk completo tem a forma padrão { ação }. A ação só roda nas linhas em que o padrão é verdadeiro.

$3 > 1000
O padrão: o campo 3 é maior que 1000?
{print $1}
A ação, só para as linhas que passaram no teste.
$ awk '$3 > 1000 {print $1}' vendas.txt
ana
davi
$ awk '$2 == "norte"' vendas.txt
ana    norte  1200.50              « sem ação, o awk imprime a linha inteira
carla  norte  450.25
$ awk '/an/' vendas.txt
ana    norte  1200.50              « padrão entre barras: linhas que contêm "an"

Você pode combinar testes com && (e) e || (ou). Exemplo: os usuários "de verdade" do sistema, que têm UID entre 1000 e 59999:

$ awk -F: '$3 >= 1000 && $3 < 60000 {print $1, $6}' /etc/passwd
ana /home/ana

Calcular: somas, médias e END

O awk tem variáveis, e elas começam valendo zero. Duas palavras especiais marcam momentos fora do "loop" de linhas:

  • BEGIN { … } roda antes da primeira linha (para cabeçalhos);
  • END { … } roda depois da última (para totais).
{s += $3}
Em cada linha, soma o campo 3 na variável s.
END
Quando as linhas acabarem…
{print "Total:", s}
…imprime o total.
$ awk '{s += $3} END {print "Total:", s}' vendas.txt
Total: 4550                      « errado! o certo é 4550.75

Os centavos sumiram. Veja por quê no próximo item.

Decimais no Ubuntu em português

Com o sistema em pt-BR, o mawk lê os números esperando o padrão brasileiro e para no ponto: 1200.50 vira 1200. Na saída, ele usa vírgula: 1137,5. Quando os dados usam ponto decimal, que é o mais comum em arquivos e logs, rode o awk com LC_NUMERIC=C na frente, para usar o padrão internacional.

$ LC_NUMERIC=C awk '{s += $3} END {print "Total:", s}' vendas.txt
Total: 4550.75
$ LC_NUMERIC=C awk '{s += $3; n++} END {print "Média:", s/n}' vendas.txt
Média: 1137.69

n++ soma 1 em n a cada linha: é um contador de linhas.

Formatar a saída: printf

O printf controla larguras e casas decimais. %-6s é um texto alinhado à esquerda em 6 posições; %8.2f é um número com 2 casas decimais em 8 posições; \n pula a linha.

$ LC_NUMERIC=C awk 'BEGIN {print "NOME   VALOR"} {printf "%-6s %8.2f\n", $1, $3}' vendas.txt
NOME   VALOR
ana     1200.50
bruno    800.00
carla    450.25
davi    2100.00

Agrupar: total por região

Uma variável com colchetes, como t[$2], guarda um valor para cada região (é um array). No END, o for (r in t) percorre todas:

$ LC_NUMERIC=C awk '{t[$2] += $3} END {for (r in t) print r, t[r]}' vendas.txt
leste 2100
norte 1650.75
sul 800

A ordem das regiões no for … in não é garantida. Se precisar, ordene depois: … | sort.


awk no dia a dia

O awk brilha na saída de outros comandos, que costuma vir em colunas:

$ df -h / | awk 'NR == 2 {print $5}'
28%                              « uso do disco principal: linha 2, coluna 5
$ ls -l Downloads | awk '{s += $5} END {print s " bytes"}'
6446140416 bytes                 « soma da coluna de tamanho do ls -l
$ ps aux | LC_NUMERIC=C awk '$3 > 2.0 {print $2, $11}'
1534 /usr/bin/gnome-shell        « processos usando mais de 2% de CPU
2210 /snap/chromium/3554/usr/lib/chromium-browser/chrome

No último exemplo, a coluna de CPU do ps usa ponto decimal (5.4). Por isso o LC_NUMERIC=C na frente do awk, como explicado acima.


Quando não funciona

"missing } near end of file"
$ awk '{print $1' vendas.txt
awk: line 2: missing } near end of file

O que aconteceu: faltou fechar a chave da ação.

Como resolver: confira se cada { tem seu } e se as aspas simples abrem e fecham.

O awk imprimiu a linha inteira em vez da coluna
$ awk "{print $1}" vendas.txt
ana    norte  1200.50
bruno  sul    800
…

O que aconteceu: o programa foi escrito entre aspas duplas. O shell trocou $1 pelo primeiro argumento do shell, que está vazio. O awk recebeu só {print }, que imprime a linha toda.

Como resolver: use sempre aspas simples no programa awk: awk '{print $1}'.

A soma ignorou os centavos

O que aconteceu: com o Ubuntu em português, o mawk para no ponto decimal ao ler 1200.50.

Como confirmar: echo "1.5 2.5" | awk '{print $1 + $2}' mostra 3, e não 4.

Como resolver: LC_NUMERIC=C awk …. Se for usar sempre, crie um apelido no ~/.bashrc: alias awk='LC_NUMERIC=C awk' (veja .bashrc, alias, PATH).

"cannot open"
$ awk '{print $1}' venda.txt
awk: cannot open "venda.txt" (No such file or directory)

O que aconteceu: o nome do arquivo está errado, ou você está em outra pasta.

Como resolver: use Tab para completar o nome e pwd para ver onde está.

As colunas vieram trocadas num CSV

O que aconteceu: sem -F, o awk separa por espaços. Num CSV, uma linha como Ana Souza,Norte,1200 vira os campos "Ana" e "Souza,Norte,1200".

Como resolver: informe o separador: awk -F, '{print $2}' dados.csv.


Referência

ItemDescriçãoExemplo
-F xSeparador de campos da entradaawk -F: '{print $1}'
-v var=valorDefine uma variável antes de começarawk -v min=100 '$3 > min'
$1, $2… $NFCampos; $NF é o último{print $NF}
$0A linha inteira{print NR, $0}
NR / NFNúmero da linha / quantidade de camposNR > 1 pula o cabeçalho
OFSSeparador da saída-v OFS=';'
BEGIN {} / END {}Antes da primeira / depois da última linhaEND {print s}
/regex/Linhas que casam com o padrãoawk '/erro/'
== != > < >= <=Comparações$2 == "sul"
&& || !E, ou, não$3 > 0 && $2 != "sul"
printfSaída formatadaprintf "%-10s %5d\n", $1, $2
length(), toupper(), tolower(), substr()Funções de texto{print toupper($1)}

Exercícios

1. Última coluna

Complete para imprimir o último campo de cada linha, qualquer que seja a quantidade de campos:

awk '{print }' dados.txt

2. Separador dois-pontos

Complete para listar os shells (campo 7) de todos os usuários do /etc/passwd:

awk '{print $7}' /etc/passwd

3. Total no fim

Complete para somar a coluna 2 e mostrar o total depois da última linha:

awk '{s += $2} {print s}' gastos.txt

4. Qual comando mostra só as linhas cujo campo 2 é "sul"?

5. No Ubuntu em português, awk '{s += $1} END {print s}' somou 1.5 e 2.5 e mostrou 3. Como corrigir?


Resumo

  • O awk divide cada linha em campos: $1, $2… $NF; $0 é a linha toda.
  • Um programa é padrão { ação }; sem ação, imprime a linha; sem padrão, roda em todas.
  • -F muda o separador; BEGIN e END rodam antes e depois das linhas.
  • Use sempre aspas simples no programa.
  • Com dados que usam ponto decimal, rode LC_NUMERIC=C awk no Ubuntu em português.