Exemplos de AWK - parte 1/3

download Exemplos de AWK - parte 1/3

of 9

description

Exemplos de como usar a linguagem AWK em plataformas Linux.Parte 1/3.

Transcript of Exemplos de AWK - parte 1/3

  • 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: halt uid:7

    username: operator uid:11

    username: root uid:0

    username: shutdown uid:6

    username: sync uid:5

    username: bin uid:1

    ...etc.

    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 { x=0 }

    /^$/ { x=x+1 }

    END { 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 [email protected].