Você está na página 1de 9

Awk em Exemplos, Parte 1

Uma introduo grande linguagem com um nome


estranho
Daniel Robbins
Presidente/CEO, Gentoo Technologies, Inc.
Dezembro de 2000
O awk uma linguagem legal com um nome bastante estranho. Em seu primeiro artigo de uma srie de
trs, Daniel Robbins ir rapidamente acelerar suas habilidades de programao awk. Conforme a srie
avana, mais tpicos sero cobertos, culminando com uma aplicao demo avanada real.

Em defesa do awk
O primeiro awk
Mltiplos campos
Scripts externos
Os blocos BEGIN e END
Expresses regulares e blocos
Expresses e blocos
Declaraes condicionais
Variveis numricas
Variveis String
Muitos operadores
Separadores de campos
Nmero de campos
Nmero de registro
Recursos
Sobre o autor

Em defesa do awk
Nesta srie de artigos, irei tornar voc um programador awk proficiente. Eu admito, o awk no tem um
nome bonito, e a verso GNU do awk, chamada gawk, tem um nome bastante estranho. Aqueles que no
so familiares com a linguagem podem ouvir "awk" e pensar em uma mistura de cdigo to retrgrado e
antiquado que capaz de levar o guru UNIX mais conhecedor loucura (fazendo com que o mesmo fique
gritando "kill -9!" enquanto corre em direo mquina de caf).
Certo, o awk no tem um grande nome. Mas uma grande linguagem. O awk foi feito para tratar
processamento de texto e gerao de relatrios, e tambm muitas funcionalidades bem projetadas que
permitem usar o mesmo para programao sria. E, diferente de algumas linguagens, a sintaxe do awk
familiar, e copia algumas das melhores partes de linguagens como o C, python, e o bash (apesar de,
tecnicamente, o awk ter sido criado antes tando do python quanto do bash). O awk uma das linguagens
que, uma vez aprendidas, torna-se parte chave do seu arsenal de codificao estratgica.

O primeiro awk

Vamos adiante e comear a brincar com o awk para ver como ele funciona. Na linha de comando, entre
com o seguinte comando:
$ awk '{ print }' /etc/passwd
Voc deve ver o contedo do seu arquivo /etc/passwd aparecer na tela. Agora, uma explicao do que o
awk fez. Quando chamamos o awk, especificamos o arquivo /etc/passwd como arquivo de entrada.
Quando executamos o awk, ele executou o comando print para cada linha no /etc/passwd, em ordem.
Toda a sada enviada a stdout, e conseguimos um resultado idntico ao de usar o comando cat sobre
/etc/passwd.
Agora, para uma explicao do bloco de cdigo { print }. No awk, as chaves so usadas para agrupar
blocos de cdigos, semelhante ao que acontece no C. Dentro de nosso bloco de cdigo, temos um nico
comando print. No awk quando um comando print aparece sozinho, o contedo completo da linha
impresso.
Aqui temos um outro exemplo do awk que faz exatamente a mesma coisa:
$ awk '{ print $0 }' /etc/passwd
No awk, a varivel $0 representa a linha corrente completa, assim, print e print $0 fazem
exatamente a mesma coisa.
Se voc quiser, pode criar um programa que ir apresentar dados sem nenhuma relao aos dados de
entrada. Aqui h um exemplo:
$ awk '{ print "" }' /etc/passwd
Onde voc passar a string "" para o comando print, impressa uma linha em branco. Se voc testar este
script, ir ver que o awk imprime uma linha em branco para cada linha em seu arquivo /etc/passwd.
Novamente, isto por que o awk executa seu script para cada linha no arquivo de entrada. Aqui h outro
exemplo:
$ awk '{ print "hiya" }' /etc/passwd
Este script ir encher a sua tela de hiya's. :)

Mltiplos campos
O awk realmente bom em tratar texto que foi dividido em mltiplos campos lgico, e permite que voc
referencie cada campo individual sem esforos adicionais, a partir do seu script awk. O seguinte script ir
imprimir uma lista de todas as contas de usurios no seu sistema:
$ awk -F":" '{ print $1 }' /etc/passwd
No exemplo acima, quando chamamos o awk, usamos a opo -F para especificar que ":" um separador
de campos. Quando o awk processa o comando print $1, ele ir imprimir o primeiro campo que
aparece em cada linha do arquivo de entrada. Segue outro exemplo:
$ awk -F":" '{ print $1 $3 }' /etc/passwd
Aqui est um trecho da sada deste script:

halt7
operator11
root0
shutdown6
sync5
bin1
...etc.
Como pode ser visto, o awk imprime o primeiro e o terceiro campos do arquivo /etc/passwd, que so o
username e uid, respectivamente. Agora, mesmo que o script funcione, ele no perfeito -- no h
nenhum espao entre os dois campos de sada! Se voc est acostumado a programar no bash ou no
python, voc esperaria que o comando print $1 $3 colocasse um espao entre os dois campos.
Entretanto, quando duas strings aparecem uma ao lado da outra em um programa awk, o awk concatena
as mesmas sem adicionar um espao intermedirio. O seguinte comando ir inserir um espao entre os
dois campos:
$ awk -F":" '{ print $1 " " $3 }' /etc/passwd
Quando voc chamar o print desta forma, ele ir concatenar $1, " ", e $3, criando uma sada legvel.
Obviamente, podemos tambm inserir algum texto descritivo se necessrio:
$ awk -F":" '{ print "username: " $1 "\t\tuid:" $3 }' /etc/passwd
Este script ir gerar a seguinte sada:
username:
username:
username:
username:
username:
username:
...etc.

halt
operator
root
shutdown
sync
bin

uid:7
uid:11
uid:0
uid:6
uid:5
uid:1

Scripts externos
Passar seus scrips para o awk como um argumento de linha de comando pode ser til para alguns scripts
"one-liners", mas quando precisamos de programas complexos, com mltiplas linhas, voc
definitivamente vai quere compor seus scrips em um arquivo externo. O awk pode ser informado para
fazer o source deste script pela opo -f:
$ awk -f myscript.awk myfile.in
Ao colocar seus scrips em seus prprios arquivost texto voc pode aproveitar algumas funcionalidades
extras do awk. Por exemplo, este script multi-linhas faz a mesma coisa que um de nossos one-liners
anterior, imprimindo o primeiro campo de cada linha no /etc/passwd:
BEGIN {
FS=":"
}
{ print $1 }
a diferena entre estes dois mtodos tem a ver em como configuramos o separador de campos. Neste
script, o separador de campos espscificado dentro do prprio cdigo (configurando a varivel FS),
enquanto nossos exemplos prvios configuravam o FS passando a opo -F":" para o awk na linha de

comando. Geralmente melhor configurar o separador de campo dentro do prprio script, simples por
que isto significa que voc tem um argumento de linha de comando a menos para lembrar de escrever.
Iremos cobrir a varivel FS com mais detalhes mais adiante, neste artigo.

Os blocos BEGIN e END


Normalmente, o awk executa cada bloco do cdigo do seu script para cada linha de entrada. Entretanto,
exsitem vrias situaes de programao em que voc pode querer executar a inicializao do cdigo
antes que o awk comece o processamento do texto do arquivo de entrada. Para estas situaes, o awk
permite que voc defina um bloco BEGIN. Usamos um bloco BEGIN no exemplo anterior. Como o bloco
BEGIN executando antes que o awk comece a processar o arquivo de entrada, um excelente lugar para
inicializar a varivel FS (field separator - separador de campo), escrever um cabealho, ou inicializar
outras variveis globais s quais voc ir se referir mais tarde no programa.
O awk tambm fornece outro bloco especial, chamado bloco END. O awk executa este bloco depois que
todas as linhas no arquivo de entrada tenham sido processadas. Tipicamente, o bloco END usado para
executar clculos finais ou imprimir resumos que devem aparecer no fim dos dados da sada.

Expresses regulares e blocos


O awk permite que se use expresses regulares para executar seletivamente um bloco individual de
cdigo, dependendo se a expresso regular combina ou no com a linha atual. Segue um script de
exemplo que imprime somente as linhas que contm a seqncia de caracteres foo:
/foo/ { print }
Obviamente, voc pode usar expresses regulares mais complicadas. Veja um exemplo que somente
imprime linhas que contenham nmeros de ponto flutuante:
/[0-9]+\.[0-9]*/ { print }

Expresses e blocos
Existem muitas outras formas de executar seletivamente um bloco de cdigo. Podemos colocar qualquer
tipo de expresso booleana antes de um bloco de cdigo para controlar quando um bloco em particular
ser executado. O awk ir executar um bloco de cdigo somente se a expresso booleana que o precede
resultar em verdadeiro. O exemplo seguinte ir imprimir o terceiro campo de todas as linhas que tem um
primeiro campo igual a fred. Se o primeiro campo da linha atual no for igual a fred, o awk ir
continuar processando o arquivo e no ir executar a declarao print para a linha corrente:
$1 == "fred" { print $3 }
O awk oferece uma seleo completa de operadores de comparao, incluindo os usuis "==", "<", ">",
"<=", ">=", e "!=". Alm disto, o awk ainda oferece os operadores "~" e "!~", que significam "combina" e
"no combina". Eles so usados especificando uma varivel no lado esquerdo do operador, e uma
expresso regular no lado direito. Veja um exmeplo que ir imprimir somente o terceiro campo da linha
se o quinto campo da mesma linha contm a seqncia de caracteres root:

$5 ~ /root/ { print $3 }

Declaraes condicionais
O awk tambm oferece uma declarao if legal, parecida com a do C. Se voc quisesse, poderia
reescrever o script anterior usando uma declarao if:
{
if ( $5 ~ /root/ ) {
print $3
}
}
Ambos os scripts funcionam de forma idntica. No primeiro exemplo, a expresso booleana colocada
fora do bloco, enquanto no segundo exemplo, o bloco executado para cada linha de entrada, e
seletivamente executamos o comando print usando uma declarao if. Os dois mtodos esto
disponveis, e voc pode escolher o que melhor se adapta s outras partes do seu script.
Aqui h um exemplo um pouco mais complicado de uma declarao if do awk. Como voc pode ver,
mesmo com testes condicionais aninhados e complexos, as declaraes if so quase idnticas s
contrapartes no C:
{
if ( $1 == "foo" ) {
if ( $2 == "foo" ) {
print "uno"
} else {
print "one"
}
} else if ( $1 == "bar" ) {
print "two"
} else {
print "three"
}
}
Usando declaraes if, podemos transformar este cdigo:
! /matchme/ { print $1 $2 $3 }
neste:
{
if ( $0 ! ~ /matchme/ ) {
print $1 $2 $3
}
}
Ambos scripts iro escrever somente as linhas que no contm a seqncia de caracters matchme.
Novamente, voc pode escolher o mtodo que funciona melhor no seu cdigo. Os dois fazem a mesma
coisa.
O awk tambm permtie o uso dos operadoers booleanos "||" (para o "ou lgico"), e "&&" (para o "e
lgico"), para permitir a criao de expresses booleanas mais complexas:

( $1 == "foo" ) && ( $2 == "bar" ) { print }


Este exemplo ir imprimir somente as linhas onde o campo um igual a foo e o campo dois igual a
bar.

Variveis numricas
At agora, somente imprimimos strings, a linha inteira, ou campos especficos. Entretanto, o awk tambm
nos permite executar clculos tanto de inteiros quanto de ponto flutuante. Usando expresses
matemticas, muito fcil escrever um script que conte o nmero de linhas em branco de um arquivo.
Aqui tem um:
BEGIN
/^$/
END

{ x=0 }
{ x=x+1 }
{ print "I found " x " blank lines. :)" }

No bloco BEGIN, ns inicializamos nossa varivel inteira para zero. A seguir, cada vez que o awk
encontra uma linha em branco, ele ir executar a declarao x=x+1, incrementando o x. Aps todas as
linhas terem sido processadas, o bloco END ser executado, e o awk ir escrever um resumo final,
especificando o nmero de linhas em branco que ele encontrou.

Variveis String
Uma das coisas legais sobre as variveis do awk que elas so simples e so stringy. Eu considero as
variveis awk como "stringy" por que todas so representadas internamente como strings. Ao mesmo
tempo, as variveis so "simples" por que voc pode executar operaes matemticas em uma varivel,
desde que ela contenha uma string de um nmero vlido, o awk ir tomar as providncias para a
converso de string para nmero. Para ver o que quero dizer, d uma olhada no seguinte exemplo:
x="1.01"
# We just set x to contain the *string* "1.01"
x=x+1
# We just added one to a *string*
print x
# Incidentally, these are comments :)
A sada o awk ser:
2.01
Interessante! Apesar de termos atribudo o valor string 1.01 para a varivel x, ainda assim conseguimos
acrescentar um a ela. No conseguiramos fazer isto no bash ou no python. A princpio, o bash no
suporta aritmtica de ponto flutuante. E, enquanto o bash possue variveis "stringy", elas no so
"simples": para executar qualquer operao matemtica, o bash exige que o clculo esteja entre os
horrveis $( ). Se estivermos usando python, teramos que converter explicitamente nossa string 1.01
para um valor de ponto flutuante antes de executar qualquer clculo sobre ele. Mesmo que isto no seja
difcil, um passo adicional. Com o awk, isto automtico, e o que torna nosso cdigo bonito e claro.
Se quizermos elevar ao quadrado e acrescentar um ao primeiro campo de cada linha de entrada,
poderamos usar o seguinte script:
{ print ($1^2)+1 }

Se voc fizer algumas experincias, ir descobrir que se uma varivel em particular no contm um
nmero vlido, o awk ir tratar a mesma como sendo zero quando estiver calculando nossa expresso
matemtica.

Muitos operadores
Outra coisa legal no awk seu complemento completo de operadores matemticos. Alm dos operadores
padro de adio, subtrao, multiplicao e diviso, o awk permite que se use o operador de
exponenciao "^", previamente demonstrado, o operador de mdulo (resto de diviso) "%", e um
punhado de outros operadoes de atribuio copiados do C.
Estes incluem o pr e ps incremento/decremento (t++, --foo), atribuio com
soma/subrao/multiplicao/diviso (a+=3, b*=2, c/=2.2, d-=6.2). Mas isto no tudo -- ainda
temos o operao atribuio conbinado ao mdulo e exponenciao tambm (a^=2, b%=4).

Separadores de campos
O awk tambm possui seu prprio complemento de variveis especiais. Algumas delas permitem que se
"afine" como o awk funciona, enquanto outras podem ser lidas para dar informaes teis sobre a entrada.
J tocamos uma destas variveis especiais, FS. Conforme mencionado anteriormente, esta varivel
permite que se configure a seqncia de caracteres que o awk espera encontrar entre os campos. Quando
estvamos usando /etc/passwk como entrada, configuramos o FS para ":". O FS permite este truque, mas
tambm permite mais flexibilidade.
O valor de FS no est limitado a um nico caracter, ele tambm pode ser configurado para ser uma
expresso regular, especificando um padro de caracteres de qualquer tamanho. Se voc est processadno
campos separados por uma ou mais tabulaes, voc vai querer configurar o FS assim:
FS="\t+"
Acima, ns usamos o caracter de expresso regular especial "+", que significa "um ou mais do caracter
anterior".
Se seus campos so separados por espaos em brancos (um ou mais espaos ou tabulaes), voc pode
querer configurar o FS para a seguinte expresso regular:
FS="[[:space:]+]"
Esta atribuio ir fazer o servio, mas desnecessria. Por qu? Por que, por padro, o FS configurado
para um caracter de espao, que o awk interpreta como "um ou mais espaos ou tabulaes'. Neste
exemplo em particular, a configurao padro do FS exatamente o que voc quer!
Expresses regulares complexas no so problema. Mesmo se seus registros so separados pela palavra
"foo", seguida de trs dgitos, a seguinte expresso regular ir permitir que seus dados sejam tratados
apropriadamente:
FS="foo[0-9][0-9][0-9]"

Nmero de campos
As prximas duas variveis que iremos tratar normalmente no so escritas, mas lidas e usadas para obter
informaes teis sobre a entrada. A primeira a varivel NF, tambm chamada de varivel "nmero de
campos". O awk ir configurar automaticamente esta varivel com o nmero de campos no registro atual.
Voc pode usar a varivel NF para apresentar somente certas linhas de entrada:
NF == 3 { print "this particular record has three fields: " $0 }
Obviamente, voc tambm pode usar a varivel NF em declaraes condicionais, como segue:
{
if ( NF > 2 ) {
print $1 " " $2 ":" $3
}
}

Nmero de registro
O nmero de registro (NR) outra varivel til. Ela sempre ir conter o nmero do registro atual (o awk
conta o primeiro registro como registro 1). At agora, ns tratamos com arquivos de entrada que
continham um registro por linha. Para estas situaes, o NR tambm ir informar o nmero da linha atual.
Entretanto, quando iniciarmos a processar registros multi-linhas mais adiante nesta srie, este no ser
mais o caso, portanto seja cuidadoso! O NR pode ser usado como a varivel NR para imprimir somente
certas linhas da entrada:
(NR < 10 ) || (NR > 100) { print "We are on record number 1-9 or 101+"
}
Outro exemplo:
{
# skip header
if ( NR > 10 ) {
print "ok, now for the real information!"
}
}
O awk fornece variveis adicionais que podem ser usadas para vrios objetivos. Iremos cobrir mais destas
variveis nos prximos artigos.
Estamos terminando nossa explorao inicial do awk. Conforme a srie prosseguir, irei demonstrar mais
funcionalidades avanadas do awk, e iremos terminar a srie com uma aplicao do mundo real.
Enquanto isto, se voc estiver com vontade de aprender mais, verifique os recursos listados abaixo.

Recursos

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.

Você também pode gostar