Processamento de texto em AWK – Parte II

Introdução

O AWK é uma linguagem utilizada em ambientes UNIX para processar dados baseados em texto. Na edição anterior apresentámos aqui um artigo de introdução a esta linguagem. Nesta edição, apresentamos alguns dos seus aspectos mais avançados.

Campos e variáveis

Nos exemplos anteriores, os acessos a campos eram sempre feitos através de um literal inteiro (do tipo $1). No entanto, este acesso também pode ser feito através de um inteiro armazenado numa variável.

Por exemplo, o seguinte programa lê um número no primeiro campo e escreve o seu conteúdo:

{ campo = $1; print $campo }

Para além disto, os campos (tal como as variáveis especiais) podem ser modificados como se fossem variáveis. Por exemplo, o seguinte programa escreve o input sem o segundo campo:

{ $2 = ""; print }

Arrays

Em AWK é possível definir arrays (apenas unidimensionais) para guardar um conjunto de valores relacionados. Apesar de apresentarem uma sintaxe semelhante aos arrays utilizados em C, existem algumas diferenças quanto à sua forma de utilização:

  • Não é necessário especificar uma dimensão para o array. Ele é ajustado automaticamente sempre que é necessário guardar um novo valor;
  • Os arrays são associativos, ou seja, funcionam como um mapa entre uma chave (o índice) e um valor.

O seguinte exemplo ilustra as vantagens da utilização deste tipo de arrays, contando as palavras (registos) no input:

{ for(i = 1; i <= NF; i++) contador[$i]++ } # O ciclo é executado para cada linha de input.
END { for(palavra in contador) print palavra, contador[palavra] }

A sintaxe do ciclo for na última linha permite iterar sobre todos os elementos de um array.

Este ciclo for no final corresponde à iteração sobre o array. Note-se que este ciclo não garante a ordem no acesso às posições do array. Note-se que este ciclo não garante a nenhuma ordem no acesso aos dados.

A instrução delete pode ser utilizada para remover um elemento do array.

Funções

As funções tem como objectivo simplificar a resolução de problemas que de outra forma se tornariam complexos. Como há funções às quais é necessário recorrer com alguma frequência, existem um conjunto de funções predefinidas e prontos a ser utilizadas.

Funções sobre números

  • sqrt(n): Devolve a raíz quadrada de n;
  • log(n): Devolve o logaritmo de base e de n;
  • exp(n): Devolve a potência de base e de n;
  • int(n): Devolve a parte inteira de n.

Exemplos:

sqrt(4) # Devolve 2
sqrt(5) # Devolve 2,23607
log(1) # Devolve 0
log(2) # Devolve 0,693147
exp(0) # Devolve 1
exp(1) # Devolve 2,71828
int(3.14159) # Devolve 3

Funções sobre strings

  • substr(string, inicio, tamanho): Devolve a substring de string que começa em inicio e tem o tamanho tamanho.
  • split(string, array, separador): Guarda em array as substrings de string separadas pela string separador. É usado um espaço como separador se este for omitido.
  • index(string, padrao): Devolve o índice da primeira ocorrência de padrao em string (ou 0 se não existir nenhuma ocorrência).

Nota: O ínicio da string corresponde ao índice 1. Da mesma forma, a função split coloca as substrings em índices a partir de 1.

Exemplos:

substr("programar", 4, 4) # Devolve "gram"
split("portugal-a-programar", palavras, "-")
print palavras[1] # portugal
print palavras[2] # a
print palavras[3] # programar
index("portugal-a-programar", "port") # Devolve 1
index("portugal-a-programar", "b") # Devolve 0

”printf” e ”sprintf”

Tem sido usada a instrução print para escrever texto no ecrã. No entanto, também é possível usar a função printf para produzir uma string formatada.

A formatação através da função printf funciona da mesma forma que em C, utilizando (principalmente) os seguintes códigos de controlo de formato:

  • %c: Escreve um char;
  • %d: Escreve um int em formato decimal;
  • %f: Escreve um float;
  • %o: Escreve um número em formato octal;
  • %s: Escreve uma string;
  • %u: Escreve um unsigned int;
  • %x: Escreve um int em formato hexadecimal.

Com isto, é possível escrever o número de cada registo do input em formatos decimal e hexadecimal com o seguinte programa:

{ printf("%d %x\n", NR, NR) }

Será produzido o seguinte texto:

1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 a
11 b
12 c
13 d
14 e
15 f
16 10
17 11
18 12
19 13
20 14

A função sprintf funciona de forma análoga, mas devolve a string em vez de a escrever.

Funções definidas pelo utilizador

Para além das funções predefinidas, é possível também definir novas funções. Estas funções devem ser definidas no início do programa (antes do primeiro bloco). Para o fazer utiliza-se a seguinte sintaxe:

function nome(parametros) {
  instrucoes
}

A instrução return indica qual o valor a ser devolvido pela função quando a sua execução termina.

Redireccionamento de output

Para além de escrever o resultado no ecrã, também é possível enviá-lo para um ficheiro ou comando. Em AWK existem três operadores relacionados com o redireccionamento de output:

  • >: Redirecciona o output para um ficheiro;
  • >>: O mesmo que o anterior, mas caso o ficheiro já exista o output é adicionado no fim;
  • |: Redirecciona o output para um comando Unix.

Por exemplo, é possível enviar o número de cada registo para um ficheiro out.txt com o seguinte programa:

{ print NR >> "out.txt" }

Conclusão

Este artigo serve de complemente ao artigo introdutório publicado na edição anterior. Este artigo pode parecer complexo para um programador não-familiarizado nesta linguagem, sendo nesse caso aconselhável a leitura do referido artigo na edição anterior.