You are on page 1of 8

Awk em Exemplos, Parte 2

Registros, laos e arrays


Daniel Robbins
Presidente/CEO, Gentoo Technologies, Inc.
Janeiro de 2001
Nesta seqncia de seu artigo anterior, introduo ao awk, Daniel Robbins continua a explorar o awk,
uma grande linguagem com um estranho nome. Daniel ir mostrar como tratar registros multi-linhas, usar
construes em lao, e criar e usar arrays no awk. No fim deste artigo, voc estar vem versado em uma
ampla gama de funcionalidades do awk, e estar pronto para escrever seus prprios scripts poderosos
awk.

Registros Multi-linhas
OFS e ORS
Multi-linhas para tabulado
Estruturas de lao
Break e Continue
Arrays
ndices strings de arrays
Ferramentas para arrays
Recursos
Sobre o autor

Registros Multi-linhas
O awk uma excelente ferrametna para ler e processar dados estruturados, como o arquivo de sistema
/etc/passwd. O arquivo /etc/passwd o banco de dados de usurios do UNIX, e um arquivo delimitado
por dois-pontos, contendo muita informao importante, incluindo todas as contas de usurio existentes e
IDs de usurio, entre outras coisas. Em meu artigo anterior, eu mostrei como o awk pode facilmente tratar
este arquivo. Tudo que precisamos fazer foi configurar a varivel FS (separador de campos) para ":".
Se a varivel FS for configurada corretamente, o awk pode ser configurado para tratar quase qualquer tipo
de dado estruturado, desde que exista um registro por linha. Entretanto, s configurar FS no serve para
nada se queremos tratar um registro que existe em mltiplas linhas. Nestas situaes, precisamos tambm
modificar a varivel RS, ou separador de registro. A varivel RS informa ao awk quando o registro atual
terminou e um novo registro comea.
Por exemplo, vamos ver como tratar a tarefa de processar uma lista de participantes do Programa Federal
de Proteo s Testemunhas:
Jimmy the Weasel
100 Pleasant Drive
San Francisco, CA 12345
Big Tony
200 Incognito Ave.
Suburbia, WA 67890
Idealmente, queremos que o awk reconhea cada endereo de trs linhas como sendo um registro
individual, em vez de trs registros separados. Tornaria nosso cdigo muito mais simples se o awk

reconhecer a primeira linha como primeiro campo ($1), o endereo como o segundo campo ($2), e a
cidade, estado e CEP como o campo $3. O cdigo abaixo ir fazer exatamente o que queremos:
BEGIN {
FS="\n"
RS=""
}
No cdigo acima, configurando FS para "\n" diz ao awk que cada campo aparece em sua prpria linha.
Configurando o RS para "", estamos informando ao awk que cada registro de endereo est separado por
uma linha em branco. Uma vez que o awk sabe como a entrada est formatada, ele pode fazer todo o
tratamento para ns, e o resto do script simples. Vamos dar uma olhada em um script completo que ir
tratar esta lista de endereos e imprimir cada registro em uma nica linha, separando cada campo com
uma vrgula.

address.awk
BEGIN {
FS="\n"
RS=""
}
{
print $1 ", " $2 ", " $3
}
Se este script for salvo como address.awk, e a lista de endereos for armazenada em um arquivo chamado
address.txt, voc pode executar este script escrevendo "awk -f address.awk address.txt". Este cdigo
produz a seguinte sada:
Jimmy the Weasel, 100 Pleasant Drive, San Francisco, CA 12345
Big Tony, 200 Incognito Ave., Suburbia, Wa 67890

OFS e ORS
Na declarao print do address.awk, voc pode ver que o awk concatena (une) as strings que so
colocadas prximas em uma linha. Usamos esta funcionalidade para inserir uma vrgula e um espao (",
") entre os trs campos de endereo que aparecem na linha. Apesar deste mtodo funcionar, ele tem um
aspecto horrvel. Ao invs de inserir ", " literais entre os campos, podemos fazer que o awk faa isto
configurando uma varivel especial do awk chamada OFS. D uma olhada no seguinte trecho de cdigo:
print "Hello", "there", "Jim!"
As vrgulas nesta linha no so parte da string literal. Em vez disto, elas informam ao awk que "Hello",
"there", e "Jim!" so campos separados, e que a varivel OFS deve ser impressa entre cada string. Por
padro, o awk produz a seguinte sada:
Hello there Jim!
Isto nos mostra que, por padro, o OFS est configurado como " ", um espao simples. Entretanto,
podemos facilmente redefinir o OFS de forma que o awk insira nosso separador de campos favorito. Aqui
est uma verso revisada do nosso programa original address.awk que usa o OFS para colocar aquelas
strings ", " intermedirias:

Uma verso revisada o address.awk


BEGIN {
FS="\n"
RS=""
OFS=", "
}
{
print $1, $2, $3
}
O awk tambm tem uma varivel especial chamada ORS, ou "separador de registros na sada".
Configurando o ORS, que , por padro, uma nova linha ("\n"), podemos controlar os caracteres que so
automaticamente impressos no fim de uma declarao print. O valor padro do ORS faz com que o awk
imprime cada nova declarao print em uma nova linha. Se quisermos que a sada tenha espaamento
duplo, podemos configurar o ORS para ser "\n\n". Ou, se queremos que os registros sejam separados por
um espao simples (e no uma nova linha), basta configurar o ORS para " ".

Multi-linhas para tabulado


Digamos que tenhamos escrito um script que converte nossa lista de endereos para uma lista com um
registro por linha, delimitado por tabulaes, para importar para uma planilha. Aps usar uma verso
levemente modificada do address.awk, torna-se claro que nosso programa funciona somente para
endereos de trs linhas. Se o awk encontrar o seguinte endereo, a quarta linha ser descartada e no ser
impressa:
Cousin Vinnie
Vinnie's Auto Shop
300 City Alley
Sosueme, OR 76543
Para tratar situaes como esta, seria interessante se noso cdigo pegar o nmero de registros por campo
em conta, imprimindo cada um em ordem. Por enquanto, o cdigo somente imprime os trs primeiros
campos do endereo. Aqui temos um cdigo que faz o que queremos:

Uma verso do address.awk que funciona com endereos com qualquer


nmero de campos
BEGIN {
FS="\n"
RS=""
ORS=""
}
{
x=1
while ( x < NF ) {
print $x "\t"
x++
}
print $NF "\n"
}

Primeiro, configuramos o separador de campos FS para "\n" e o separador de registros RS para "", de
forma que o awk trata os endereos multi-linhas corretamente, como antes. A seguir, configuramos o
separador de registros de sada, ORS, para "", o que vai fazer com que a declarao print no coloque um
"nova linha" ao fim de cada chamada. Isto significa que se queremos que algum texto inicie em uma nova
linha, temos de escrever explicitamente um print "\n".
No cdigo principal, criamos uma varivel chamada x que guarda o nmero do registro atual que estamos
processando. Inicialmente este nmero 1. A seguir, usamos um lao while (uma estrutura de loop do
awk que idntica que encontrada na linguagem C) para iterar por todos os registros, menos o ltimo,
imprimindo o registro e um caracter de tabulao. Finalmente, imprimimos o ltimo registro e uma novalinha literal. Novamente, uma vez que o ORS est configurado para "", o print no ir acrescentar novalinhas para ns. A sada do programa parecida com o que segue, que exatamente o que queramos:

Nossa sada pretendida. No bonita, mas delimitada por tabulaes


para facilitar importao para uma planilha
Jimmy the Weasel
100 Pleasant Drive
San Francisco, CA 12345
Big Tony
200 Incognito Ave.
Suburbia, WA 67890
Cousin Vinnie Vinnie's Auto Shop
300 City Alley Sosueme, OR 76543

Estruturas de lao
Ns j vimos a estrutura de lao while do awk, que idntica sua contraparte no C. O awk tambm tem
uma estrutura de lao "do..while" que testa a condio no fim do bloco de cdigo, em vez de no incio,
como um lao while padro. similar aos laos "repeat..until" que podem ser encontrados em outras
linguagens. Veja um exemplo:

Exemplo do...while
{
count=1
do {
print "I get printed at least once no matter what"
} while ( count != 1 )
}
Como a condio testada aps o bloco de cdigo, um lao "do...while", diferente de um lao while
normal, sempre ir executar o bloco de cdigo pelo menos uma vez. Por outro lado, um lao while nunca
ser executado se sua condio falsa quando o lao encontrado.

Laos for
O awk permite que se crie laos for, que, como os laos while, idntico sua contraparte C:
for ( initial assignment; comparison; increment ) {
code block
}
Aqui temos um exemplo:
for ( x = 1; x <= 4; x++ ) {
print "iteration", x
}

Este trecho ir escrever:


iteration
iteration
iteration
iteration

1
2
3
4

Break e Continue
Novamente, exatamente como no C, o awk tem as declaraes break e continue. Estas declaraes
permitem um melhor controle sobre as vrias estruturas de lao do awk. Segue um trecho de cdigo que
precisa desesperadamente uma declarao break:

Um lao while infinito


while (1) {
print "forever and ever..."
}
Como o 1 sempre verdadeiro, este lao ser executado para sempre. Aqui h um lao que executado
somente dez vezes:

Um exemplo da declarao break


x=1
while(1) {
print "iteration",x
if ( x == 10 {
break
}
x++
}
Aqui, a declarao break usada para parar o lao mais interno. O break faz com que o lao seja
terminado imediatamente e a execuo do programa continue na linha aps o cdigo do lao.
A declarao continue complementa o break, e funciona assim:
x=1
while (1) {
if ( x == 4 ) {
x++
continue
}
print "iteration", x
if ( x > 20 ) {
break
}
x++
}
Este cdigo ir imprimir "iteration 1" at "iteration 21", exceto por "iteration 4". Se a iterao 4, x
incrementado e a declarao continue chamada, o que faz com que o awk imediatamente inicie o
prximo lao sem executar o resto do bloco de cdigo. A declarao continue funciona para todos os

tipos de laos iterativos, da mesma forma que o break. Quando usado no corpo de um lao for, o continue
faz com que a varivel de controle de lao seja automaticamente incrementado. Aqui h um lao for
equivalente:
for ( x=1; x<=21; x++ ) {
if ( x == 4 ) {
continue
}
print "iteration", x
}
No foi necessrio incrementar o x antes de chamar o continue, como no nosso lao while, j que o lao
for incrementa o x automaticamente.

Arrays
Voc vai ficar feliz de saber que o awk possui arrays. Entretanto, no awk, costume iniciar ndices de
arrays em 1, em vez de 0:
myarray[1]="jim"
myarray[2]=456
Quando o awk encontra a primeira atribuio, myarray criado e o elemento myarray[1] passa a ser
"jim". Depois que a segunda atribuio processada, o array possui dois elementos.

Iteraes sobre arrays


Uma vez definido, o awk possui um mecanismo bastante til para fazer iteraes sobre os elementos de
um array, como segue abaixo:
for ( x in myarray ) {
print myarray[x]
}
Este cdigo ir escrever todos os elementos do array myarray. Quando voc usa esta forma especial
"in" do lao for, o awk ir atribuir todos os ndices existentes de myarray a x (a varivel de controle de
lao) a cada vez, executando o bloco de cdigo do lao aps cada atribuio. Apesar de ser uma
funcionalidade awk bastante til, ela tem um problema -- quando o awk passa pelos ndices do array, ele
no segue nenhuma ordem em particular. Isto significa que no h jeito de saber se a sada do cdigo
acima ser:
jim
456
ou
456
jim
Parafraseando Forrest Gump, fazer iteraes sobre o contedo de um array como uma caixa de
chocolates -- voc nunca sabe o que vai obter. Isto tem a ver com o jeito de string dos arrays do awk, que
iremos ver agora.

ndices strings de arrays


Em meu artigo anterior, eu mostrei que o awk armazena valores numricos em um formato string. Apesar
do awk executar as converses necessrias para isto funcionar, isto abre a porta para certos cdigos
estranhos:
a="1"
b="2"
c=a+b+3
Depois que o cdigo executado, o c igual a 6. Como o awk "stringy", somar as strings "1" e "2"
funcionalmente igual a somar os nmeros 1 e 2. Em ambos os casos, o awk ir fazer a soma. A natureza
"stringy" do awk bastante intrigante -- voc pode se perguntar o que acontece se usarmos ndices string
para arrays. Por exemplo, veja o seguinte cdigo:
myarr["1"]="Mr. Whipple"
print myarr["1"]
Como voc deve esperar, este cdigo ir escrever "Mr. Whipple". Mas e se tirarmos as aspas que esto
em torno do segundo ndice "1"?
myarr["1"]="Mr. Whipple"
print myarr[1]
Adivinhar o resultado deste trecho de cdigo um pouco mais difcil. O awk ir considerar
myarr["1"] e myarr[1] como sendo dois elementos seprados do array, ou eles se referem ao mesmo
elemento? A resposta que eles se referem ao mesmo elemento, e o awk ir escrever "Mr. Whipple",
como no primeiro trecho de cdigo. Apesar disto parecer estranho, por trs das cenas o awk tem usado
ndices strings para seus arrays todo este tempo"
Depois de aprender este estranho fato, alguns podem se sentir tentados a usar um cdigo esquisito como
este:
myarr["name"]="Mr Whipple"
print myarr["name"]
No s este cdigo no resulta em erro, mas funcionalmente idntico aos nossos exemplos anteriores, e
ir escrever "Mr. Whipple" da mesma forma! Como voc pode ver, o awk no nos limita a usar ndices
com inteiros puros, podemos usar ndices string se quisermos, sem criar nenhum problema. Onde quer
que usemos ndices de array no-inteiros, como myarr["name"], estamos usando arrays associativos.
Tecnicamente, o awk no est fazendo nada diferente nos bastidores quando usamos um ndice string
(uma vez que mesmo que voc usar um ndice "inteiro", o awk ainda ir tratar ele como string).
Entretanto, voc ainda assim deve cham-los de arrays associativos -- soa mais interesssante e ir
impressionar seu chefe. O ndice stringy ser nosso pequeno segredo. :)

Ferramentas para arrays


Quando se trata de arrays, o awk nos d bastante flexibilidade. Podemos usar ndices string, e no somos
obrigados a usar seqncias numricas contnuas de ndices (por exemplo, podemos definir myarr[1] e
myarr[1000], e deixar todos os outros elementos indefinidos). Apesar de isto poder ser bastante til,
em algumas circunstncias pode causar confuses. Felizmente, o awk oferece um par de funcionalidades
para ajudar a tornar os arrays mais gerenciveis.

Primeiro, podemos excluir elementos de arrays. Se voc quer excluir o elemento 1 do seu array
fooarray, escreva:
delete fooarray[1]
E, se voc quer ver se um determinado elemento do array existe, pode usar o operador booleano especial
"in" conforme segue:
if ( 1 in fooarray ) {
print "Ayep! It's there."
} else {
print "Nope! Can't find it."
}

Na prxima vez
Ns j cobrimos bastante cho neste artigo. Na prxima vez, eu irei arredondar seu conhecimento awk
mostrando como usar as funes matemticas e de string do awk e como criar suas prprias funes.
Tambm irei percorrer os passos da criao de um programa de balano de cheques. At l, eu encorajo
voc a escrever alguns programas awk seus, e checar os seguintes recursos.

Recursos

Leia o Awk em exemplos, Parte 1


Se voc do tipo que prefere um livro, o sed & awk, 2nd edition uma escolha excelente.
Certifique-se de checar o FAQ do comp.lang.awk. Ele tambm muitos links adicionais sobre o
awk.
O awk tutorial, de Patric Hartigan, vem com muitos scripts awk teis.
O Thompson's TAWK Compiler, compila scripts awk em executveis binrios bem rpidos.
Existem verses disponveis para Windows, OS/2, DOS e UNIX.
O GNU Awk User's Guide est disponvel como referncia online.

Sobre o autor
Residindo em Albuquerque, New Mexico, Daniel Robbins o Presidente/CEO da Gentoo Technologies,
Inc., o criador do Gentoo Linux, um Linux avanado para o PC, e o sistema Portage, a prxima gerao
de sistema de ports para o Linux. Ele tambm tem servido como autor para os livros da Macmillan
Caldera OpenLinux Unleashed, SuSE Linux Unleashed, e Samba Unleashed. Daniel est envolvido com
computadores de alguma forma desde o segundo grau, quando foi exposto pela primeira vez para a
linguagem de programao Logo, bem como a uma dose perigosa de Pac Man. Isto provavelmente
explica por que ele tem trabalhado como Lead Graphic Artist na SONY Electronic
Publishing/Psygnosis. Daniel gosta de gastar seu tempo com sua esposa, Mary, e sua nova filhinha,
Hadassah. Voc pode entrar em contato com Daniel no email drobbins@gentoo.org.

You might also like