O AWK é uma linguagem de programação criada nos anos 70 com o objectivo de processar dados baseados em texto. Esta linguagem baseia-se fortemente na manipulação de strings e no uso de expressões regulares.
Porquê usar AWK
O AWK tem a vantagem de permitir executar tarefas simples sobre texto utilizando programas mais compactos que os equivalentes escritos em linguagens imperativas como a linguagem C. Isto acontece devido à inexistência de uma função main e de declaração de variáveis.
Estrutura de um programa em AWK
Os programas em AWK são constituídos por uma série de pares condição-acção, com a seguinte sintaxe:
condição { acção }
O programa vai ler o input sob a forma de registos (geralmente cada registo corresponde a uma linha) e dividir cada registo em campos (geralmente separados por espaços). Após isto, a condição
vai ser testada para cada registo e nos casos em que seja verdadeira é executada a acção
.
Exemplo simples: Hello World
Um programa “Hello World” em AWK tem o seguinte aspecto:
BEGIN { print "Hello World" }
Em que print
é uma palavra reservada que escreve na saída os seus argumentos e BEGIN
é uma condição que indica que a acção correspondente deve ser executada antes de percorrer o input (da mesma forma, existe um condição END
cuja acção é executada no fim do input).
Este programa pode ser executado através de uma linha de comandos da seguinte forma:
awk 'BEGIN { print "Hello World" }'
Ou, sendo hello.awk
um ficheiro contendo o código a executar:
awk -f hello.awk
Neste caso não existe input, mas caso exista ele pode ser enviado através do standard input.
Campos e variáveis
Como já foi dito, quando um registo é lido a partir do input, ele é dividido em campos (separados por espaços, por omissão). Cada um desses campos pode ser acedido através de uma variável de campo da forma $N
, em que N é o número do campo ($1
representa o primeiro campo, $2
o segundo e assim sucessivamente). Existe o caso particular de $0
que contém todo o registo.
Por exemplo, a seguinte acção escreve apenas o segundo campo de cada registo:
print $2
Para além das variáveis de campo é possível definir e manipular variáveis (geralmente para guardar valores intermédios). Estas variáveis não têm associado um tipo e são automaticamente inicializadas com 0
ou ""
(string vazia), dependendo da forma como forem utilizadas. Existem ainda, definidas por omissão as seguintes variáveis especiais:
NF
: Número de campos do registo actual;NR
: Número do registo actual;FS
: Separador de campos (por omissão, espaço);RS
: Separador de registos (por omissão, newline).
Por exemplo, a seguinte acção escreve o último e o primeiro campo de cada registo:
print $NF,$1
Condições e expressões regulares
As condições podem ser de diversos tipos, como expressões lógicas e aritméticas (envolvendo variáveis de campo ou variáveis especiais). No entanto, o mais frequente é utilizar uma expressão regular como condição.
As expressões regulares permitem descrever de uma forma flexível padrões em strings, sendo úteis para filtrar apenas as linhas que contenham uma determinada palavra. Por exemplo, a seguinte condição define as linhas do input contendo a string programar
:
/programar/
Neste caso a expressão regular equivale a uma string, mas podem ser usadas expressões regulares mais complexas:
/^programar/
: Registos que começam comprogramar
;/programar$/
: Registos que terminam comprogramar
;/^programar$/
: Registos que sejam iguais aprogramar
;/[Pp]rogramar/
: Registos que contêmProgramar
ouprogramar
;/p@p|programar/
: Registos contendop@p
ouprogramar
;/[a-z]/
: Registos contendo uma letra minúscula;/[^a-z]/
: Registos não contendo uma letra minúscula;/[a-zA-Z]/
: Registos contendo uma letra maiúscula ou minúscula.
Estas condições procuram a expressão regular em todo o registo. Para a procurar apenas num campo utiliza-se o operador ~
. Por exemplo, a seguinte condição verifica se o segundo campo do registo contém uma letra maiúscula:
$2~[A-Z]
É também possível definir duas condições para delimitar um bloco de texto. Por exemplo, a seguinte condição define o bloco entre duas linhas que começam com programar
:
/^programar/,/^programar/
Exemplos de programas em AWK
Em seguida estão alguns exemplos simples de programas escritos em AWK:
Contagem de palavras
BEGIN { words = 0 } { words += NF # Cada palavra é um campo. } END { print words }
Contagem de linhas não-vazias
BEGIN { lines = 0 } NF>0 { lines++ } END { print lines }
Soma de uma lista de números (um número por linha)
BEGIN { sum = 0 } { sum += $1 } END { print sum }
O mesmo que a anterior, mas somando apenas os números menores que 20
BEGIN { sum = 0 } $1<20 { sum += $1 } END { print sum }
Média de uma lista de números
BEGIN { sum = 0 } { sum += $1 } END { print sum/NR }
Números das linhas que contêm a palavra “programar” (case-sensisitve)
/programar/ { print NR }
Números das linhas que contêm a palavra “programar” (case-insensisitve)
/[Pp][Rr][Oo][Gg][Rr][Aa][Mm][Aa][Rr]/ { print NR }
Selecção das linhas ímpares
NR%2==1 { print }
Selecção apenas das linhas entre “INÍCIO” e “FIM”
/^INÍCIO$/,/^FIM$/ { print }
Estes são apenas exemplos simples com o objectivo de indicar as capacidades do AWK. É no entanto possível construir programas mais complexos contendo vários pares condição-acção, utilizando variáveis para guardar parte de registos ou alterando os separadores de campo e registo. Estes tópicos mais avançados não são abrangidos neste artigo e podem ser consultados no guia do utilizador, disponível em http://www.gnu.org/software/gawk/manual.