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.
- 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.50como1200, 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ável | Na linha "ana norte 1200.50" | Significado |
|---|---|---|
$1 | ana | Primeiro campo |
$2 | norte | Segundo campo |
$3 | 1200.50 | Terceiro campo |
$0 | ana norte 1200.50 | A linha inteira |
NF | 3 | Number of Fields: quantos campos a linha tem |
$NF | 1200.50 | O último campo |
NR | 1 | Number of Record: o número da linha |
Imprimir uma coluna
- awk
- O programa.
- '{
- Abre a ação. As aspas simples protegem o
$1do 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
| Ferramenta | Melhor quando… |
|---|---|
cut -d ";" -f 2 | O 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ções | Você 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.
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
$ 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.
$ 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}'.
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).
$ 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á.
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
| Item | Descrição | Exemplo |
|---|---|---|
-F x | Separador de campos da entrada | awk -F: '{print $1}' |
-v var=valor | Define uma variável antes de começar | awk -v min=100 '$3 > min' |
$1, $2… $NF | Campos; $NF é o último | {print $NF} |
$0 | A linha inteira | {print NR, $0} |
NR / NF | Número da linha / quantidade de campos | NR > 1 pula o cabeçalho |
OFS | Separador da saída | -v OFS=';' |
BEGIN {} / END {} | Antes da primeira / depois da última linha | END {print s} |
/regex/ | Linhas que casam com o padrão | awk '/erro/' |
== != > < >= <= | Comparações | $2 == "sul" |
&& || ! | E, ou, não | $3 > 0 && $2 != "sul" |
printf | Saída formatada | printf "%-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:
2. Separador dois-pontos
Complete para listar os shells (campo 7) de todos os usuários do /etc/passwd:
3. Total no fim
Complete para somar a coluna 2 e mostrar o total depois da última linha:
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. -Fmuda o separador;BEGINeENDrodam antes e depois das linhas.- Use sempre aspas simples no programa.
- Com dados que usam ponto decimal, rode
LC_NUMERIC=C awkno Ubuntu em português.