Programação em AWK

Blau Araujo

Conteúdo das aulas

Playlist do curso

Download da playlist

Depende do programa yt-dlp:

yt-dlp https://www.youtube.com/playlist?list=PLXoSGejyuQGroZqA8T7P8_T-qR-islONm

1. A linguagem

Table 1: Resumo sobre a linguagem AWK
Características  
Criadores Alfred (A)ho, Peter (W)einberger e Brian (K)ernighan.
Motivação Escrever programas simples e curtos para a linha de comandos.
Lançamento 1977 (Unix V7)
Sintaxe Inspirada na linguagem C.
Execução Linguagem interpretada.
Filtro Funciona como um utilitário que processa fluxos de texto.
Propósito Tarefas de processamento e manipulação de dados em texto.
Secundariamente Processamento de fluxos de texto em geral.
Binário atual nawk (Software Livre, mantido por Brian Kernighan)
Cartoon com trogloditas e humanos modernos sapiens dizendo as mesmas palavras, apesar da distância no tempo: awk, sed, grep e pwd
Figure 1: A evolução da linguagem humana (não sei a quem creditar)

1.1 - Implementações mais importantes

Original
Lançada em 1977 com o Unix V7.
NAWK

Atualização do AWK original iniciada em 1985 e descrita no livro The AWK Programming Language, publicado em 1988. Em 1996 foi tornada um Software Livre e é mantida até hoje por Brian Kernighan.

Pacote Debian: original-awk

The_AWK_Programming_Language.jpg
Figure 2: The AWK Programming Language - 1st Edition (1988)
awk2cover.png
Figure 3: The AWK Programming Language - 2nd Edition (2023)
GAWK

Implementação livre para o Projeto GNU. Descrição publicada online e no livro Effective AWK Programming, de Arnold Robbins.

Pacote Debian: gawk

gawk-guide_.jpg
Figure 4: Effective AWK Programming
MAWK

Implementação feita por Mike Brennan com o propósito de ser extremamente rápida. É a implementação instalada por padrão no Debian GNU/Linux.

Pacote Debian: mawk

1.2 - Estrutura básica dos programas

Por padrão, os programas em awk são escritos para lerem dados recebidos pela entrada padrão ou pela leitura de arquivos cujos nomes forem passados como argumentos na invocação do interpretador. Cada linha de dados lida é tratada como um registro e só será processada se houver algum casamento entre seu conteúdo e uma das regras associadas a um bloco de ações.

Portanto, um código em awk sempre será escrito em pares de regras e ações.

Bloco de ações

O código a ser executado no casamento de uma regra. Havendo uma regra definida, a ação padrão por omissão é imprimir o registro lido (equivale à ação { print }).

Regras

Expressões que determinam se, ou quando, o bloco de ações será executado.

/REGEX/
Uma expressão regular descrevendo o padrão de texto buscado nas linhas recebidas pela entrada padrão (registros).
Expressão
Qualquer expressão de comparação, lógica ou aritmética que resulte em zero (falso) ou diferente de zero (verdadeiro).
regra-inicial, regra-final
Um par de expressões, separadas por vírgula, especificando uma faixa de registros.
BEGIN/END
Regras especiais para definir ações que devem ser executadas no antes (BEGIN) e depois (END) da leitura de todos os registros. Um código contendo apenas a regra BEGIN pode ser executado sem a leitura de dados na entrada padrão (arquivos, pipes, etc).
BEGINFILE/ENDFILE
Regras especiais para definir as ações que devem ser executadas antes (BEGINFILE) e depois (ENDFILE) da leitura de cada arquivo.
Regra vazia (sem regra)
A regra vazia casa com todos os registros lidos.

1.3 - Execução dos programas

Na linha de comandos

Na linha de comandos, o código do programa é um argumento da invocação do awk e, geralmente, é escrito entre aspas simples para evitar que o shell expanda seu conteúdo.

awk [OPÇÕES] 'regra { ações } regra {ações} ...' [ARQUIVOS]

Em arquivos de código

Os programas em awk também podem ser escritos em arquivos:

regra { ações }
regra { ações }
...

Arquivos de programas em awk podem ser executados como argumentos da invocação explícita do interpretador ou pela invocação implícita em uma hashbang, desde que o arquivo tenha permissão de execução.

Invocação explícita
awk -f programa.awk [ARQUIVOS]
Hashbang para o interpretador padrão do sistema
#!/usr/bin/env -S awk -f
Hashbang para um interpretador específico
#!/bin/gawk -f
Execução de arquivos executáveis
./programa.awk [ARQUIVOS]

1.4 - Primeiro contato

Salve, simpatia!

Execução de código independente da passagem de dados na entrada ou do nome de um arquivo
awk 'BEGIN { print "Salve, simpatia!" }'

Saída:

Salve, simpatia!

Passando uma linha de texto pela entrada padrão (regra "vazia")
echo 'Salve, simpatia!' | awk '{print "O registo " NR " contém o texto: " $0}'

Saída:

O registo 1 contém o texto: Salve, simpatia!

Passando uma linha de texto pela entrada padrão (regra sem ação)
echo 'Salve, simpatia!' | awk '1'

Saída:

Salve, simpatia!

Isso não significa que podemos omitir regras e ações ao mesmo tempo
echo 'Salve, simpatia!' | awk ''; echo $?

Saída:

0

Processamento de dados em tabelas

Arquivo exemplos/compras1.txt:

Item      Preço   Qtd   Unidade
Banana     5,00     3       1kg
Arroz     15,00     5       2kg
Feijão    12,00    10       1kg
Leite      4,00     4        1l
Imprimir apenas um registro
awk 'NR == 2' exemplos/compras1.tab

Saída:

Banana     5,00     3       1kg

Imprimir tudo a partir do registro 2
awk 'NR > 1' exemplos/compras1.tab

Saída:

Banana     5,00     3       1kg
Arroz     15,00     5       2kg
Feijão    12,00    10       1kg
Leite      4,00     4        1l

Total gasto com cada item
awk 'NR > 1 {print $1 ": R$" $2 * $3}' exemplos/compras1.tab

Saída:

Banana: R$15
Arroz: R$75
Feijão: R$120
Leite: R$16

Total de unidades a comprar (com saídas formatadas)
awk 'NR > 1 { printf "%6-s: %d",$1,$3 * $4; gsub(/[0-9]/,"",$4); print $4}' exemplos/compras1.tab

Saída:

Banana: 3kg
Arroz : 10kg
Feijão: 10kg
Leite : 4l

Valor total da compra
awk 'NR > 1 {itens++; total+=$2*$3} END {print "Valor total: R$" total}' exemplos/compras1.tab

Saída:

Valor total: 226

2. AWK em scripts

Nós podemos trabalhar com o AWK em scripts de duas formas básicas:

Scripts totalmente em AWK

Todo o código do script é escrito na linguagem em AWK e eventuais comandos do shell são executados com pipes ou pela função interna system().

#!/bin/gawk -f

BEGIN {
    print "Salve, simpatia!"
}
Scripts em shell com invocações do AWK

A forma mais comum e flexível de criar scripts com o AWK, porque alia o poder dos recursos do shell com as funcionalidades da linguagem AWK.

#!/bin/bash

awk 'BEGIN {print "Salve, simpatia!}'

Passagem de arquivos para scripts totalmente em AWK

Em princípio todos os argumentos na invocação de um script em AWK são interpretados como nomes de arquivos.

Arquivo nomes.txt:

Aho, Alfred
Weinberger, Peter
Kernighan, Brian

Script salve.awk:

#!/bin/gawk -f

{
    print "Salve, " $2 "!"
}

Executando o script com um nome de arquivo como argumento:

:~$ ./salve.awk nomes.txt

Saída:

Salve, Alfred!
Salve, Peter!
Salve, Brian!

É possível fazer com que o código interprete a passagem de qualquer palavra na invocação de scripts em AWK como argumentos genéricos (vetores ARGV e ARGC), mas este é um assunto para mais adiante.

Passagem de variáveis do shell para scripts em AWK

Opção -v
Com a opção -v NOME=VALOR, nós podemos passar a variável NOME para scripts em AWK:
:~$ awk -v usr=$USER 'BEGIN {print "O seu nome de usuário é " usr "!"}'
O seu nome de usuário é blau!

Também funciona com scripts (username.awk):

#!/bin/gawk -f

BEGIN {
    print "Seu nome de usuário é " usr "!"
}

Executando:

:~$ ./username.awk -v usr=$USER
Seu nome de usuário é blau!
Variáveis diretas
Variáveis também podem ser passadas do shell para o script em AWK por atribuições feitas diretamente antes de nomes de arquivos:
:~$ 'BEGIN {print var}' var=banana

:~$ awk 'BEGINFILE {print var}' var=banana nomes.txt
banana

Como essas atribuições precisam ser feitas antes de nomes de arquivos, elas só poderão ser avaliadas em regras e ações relacionadas com arquivos!

Exportação de variáveis
Variáveis também podem ser passadas através do mecanismo da exportação. Para isso, o script deve avaliar o nome da variável exportada como chave do vetor associativo ENVIRON:

Script environ.awk:

#!/bin/gawk -f

BEGIN {
    print "O valor da variável exportada \042fruta\042 é \042" ENVIRON["fruta"] "\042"
}

Executando:

:~$ fruta=pitanga ./environ.awk
O valor da variável exportada "fruta" é "pitanga"

Apoie o meu trabalho