Compartilhe esta página

Aprenda X em Y Minutos

Onde X=awk

AWK é uma ferramenta padrão em todos os sistemas UNIX compatíveis com POSIX. É como um Perl despojado, perfeito para tarefas de processamento de texto e outras tarefas de script. Possui uma sintaxe C-like, mas sem ponto e vírgula, gerenciamento manual de memória, ou tipagem estática. Destaca-se no processamento de texto. Você pode chamá-lo a partir de um shell-script, ou você pode usá-lo como uma linguagem de script autônomo.

Por que usar AWK ao invés de Perl? Principalmente porque AWK faz parte do UNIX. Você pode sempre contar com ele, enquanto o futuro do Perl é indefinido. AWK é também mais fácil de ler que Perl. Para scripts simples de processamento de texto, particularmente aqueles que leem arquivos linha por linha e fatiam texto por delimitadores, AWK é provavelmente a ferramenta certa para a tarefa.

#!/usr/bin/awk -f

# Comentários são assim

# Programas AWK consistem de uma coleção de padrões e ações. O mais
# importante padrão é chamado BEGIN. Ações estão dentro de blocos
# entre chaves.

BEGIN {

    # O bloco BEGIN será executado no começo do programa. É onde você coloca
    # todo código que prepara a execução, antes que você processe qualquer
    # arquivo de texto. Se você não tem arquivos de texto, então pense no
    # BEGIN como o ponto principal de entrada.

    # Variáveis são globais. Simplesmente atribua valores ou as use, sem
    # necessidade de declarar.

    # Operadores são como em C e similares
    a = count + 1
    b = count - 1
    c = count * 1
    d = count / 1 # divisão inteira
    e = count % 1 # módulo
    f = count ^ 1 # exponenciação

    a += 1
    b -= 1
    c *= 1
    d /= 1
    e %= 1
    f ^= 1

    # Incrementando e decrementando por um
    a++
    b--

    # Como um operador pré-fixado, retorna o valor incrementado
    ++a
    --b

    # Perceba, não há pontuação, como ponto-e-vírgula, ao final das declarações

    # Declarações de controle
    if (count == 0)
        print "Começando com count em 0"
    else
        print "Como é que é?"

    # Ou você pode usar o operador ternário
    print (count == 0) ? "Começando com count em 0" : "Como é que é?"

    # Blocos multilinhas devem usar chaves
    while (a < 10) {
        print "Concatenação de texto é feita" " com uma série" " de"
            " textos separados por espaço"
        print a

        a++
    }

    for (i = 0; i < 10; i++)
        print "Uma boa opção para um loop de uma linha"

    # Quanto a comparações, eis os padrões:
    a < b   # Menor que
    a <= b  # Menor ou igual a
    a != b  # Não igual
    a == b  # Igual
    a > b   # Maior que
    a >= b  # Maior ou igual a

    # Bem como operadores lógicos
    a && b  # E
    a || b  # OU (inclusivo)

    # Em adição, há o utilíssimo operador para expressões regulares
    if ("foo" ~ "^fo+$")
        print "Fooey!"
    if ("boo" !~ "^fo+$")
        print "Boo!"

    # Matrizes
    arr[0] = "foo"
    arr[1] = "bar"
    # Infelizmente, não há outra forma para inicializar uma matriz. Apenas
    # coloque cada valor em uma linha, como mostrado acima.

    # Você também pode ter matrizes associativas
    assoc["foo"] = "bar"
    assoc["bar"] = "baz"

    # E matrizes multidimensionais, com algumas limitações que não mencionarei
    multidim[0,0] = "foo"
    multidim[0,1] = "bar"
    multidim[1,0] = "baz"
    multidim[1,1] = "boo"

    # Você pode testar a pertinência de um elemento em uma matriz
    if ("foo" in assoc)
        print "Fooey!"

    # Você pode também usar o operador 'in' para percorrer as chaves de uma
    # matriz associativa
    for (key in assoc)
        print assoc[key]

    # Os argumentos da linha de comando estão em uma matriz especial ARGV
    for (argnum in ARGV)
        print ARGV[argnum]

    # Você pode remover elementos de uma matriz
    # Isso é muito útil para prevenir que o AWK assuma que os argumentos são
    # arquivo para ele processar
    delete ARGV[1]

    # A quantidade de argumentos passados está na variável ARGC
    print ARGC

    # O AWK tem várias funções nativas. Elas estão separadas em três categorias.
    # Demonstrarei cada uma delas logo mais abaixo.

    return_value = arithmetic_functions(a, b, c)
    string_functions()
    io_functions()
}

# Eis como você deve definir uma função
function arithmetic_functions(a, b, c,    d) {

    # Provavelmente a parte mais irritante do AWK é ele não possuir variáveis
    # locais. Tudo é global. Para pequenos scripts, isso não é problema, e
    # pode até mesmo ser considerado útil, mas para grandes scripts, isso pode
    # ser um problema.

    # Mas há como contornar isso (um hack). Os argumentos de função são locais
    # para a função e o AWK permite que você defina mais argumentos de função
    # do que ele precise. Então, coloque a variável local na declaração de
    # função, como eu fiz acima. Como uma convenção, adicione alguns espaços
    # extras para distinguir entre parâmetros de função reais e variáveis
    # locais. Neste exemplo, a, b e c são parâmetros reais, enquanto d é
    # meramente uma variável local.

    # Agora, serão demonstradas as funções aritméticas

    # Muitas implementações AWK possuem algumas funções trigonométricas padrão
    localvar = sin(a)
    localvar = cos(a)
    localvar = atan2(a, b) # arco-tangente de b / a

    # E conteúdo logarítmico
    localvar = exp(a)
    localvar = log(a)

    # Raiz quadrada
    localvar = sqrt(a)

    # Descartando a parte não inteira de um número em ponto flutuante.
    localvar = int(5.34) # localvar => 5

    # Números aleatórios
    srand() # Forneça uma semente como argumento. Por padrão, ele usa a hora atual
    localvar = rand() # Número aleatório entre 0 e 1.

    # Aqui mostramos como retornar um valor
    return localvar
}

function string_functions(    localvar, arr) {

    # Sendo o AWK uma linguagem para processamento de texto, ele possui
    # várias funções para manipulação de texto, muitas das quais
    # fortemente dependentes de expressões regulares.

    # Procurar e substituir, primeira instância (sub), ou todas (gsub)
    # Ambas retornam o número de instâncias substituídas
    localvar = "fooooobar"
    sub("fo+", "Meet me at the ", localvar) # localvar => "Meet me at the bar"
    gsub("e+", ".", localvar) # localvar => "m..t m. at th. bar"

    # Localiza um texto que casa com uma expressão regular
    # index() faz a mesma coisa, mas não permite uma expressão regular
    match(localvar, "t") # => 4, pois 't' é o quarto carácter

    # Separa por delimitador
    split("foo-bar-baz", arr, "-") # a => ["foo", "bar", "baz"]

    # Outras coisas úteis
    sprintf("%s %d %d %d", "Testing", 1, 2, 3) # => "Testing 1 2 3"
    substr("foobar", 2, 3) # => "oob"
    substr("foobar", 4) # => "bar"
    length("foo") # => 3
    tolower("FOO") # => "foo"
    toupper("foo") # => "FOO"
}

function io_functions(    localvar) {

    # Você já viu como imprimir
    print "Hello world"

    # Também há o printf
    printf("%s %d %d %d\n", "Testing", 1, 2, 3)

    # O AWK não disponibiliza manipuladores de arquivo. Ele irá automaticamente
    # manipular um arquivo quando você fizer algo que precise disso. O texto
    # que você usou para isso pode ser usado como um manipulador de arquivo,
    # para propósitos de E/S. Isso faz ele parecer com um shell script:

    print "foobar" >"/tmp/foobar.txt"

    # Agora a string "/tmp/foobar.txt" é um manipulador de arquivos. Você pode
    # fechá-lo:
    close("/tmp/foobar.txt")

    # Aqui está como você pode executar alguma coisa no shell
    system("echo foobar") # => prints foobar

    # Lê uma linha da entrada padrão e armazena em localvar
    getline localvar

    # Lê uma linha de um pipe
    "echo foobar" | getline localvar # localvar => "foobar"
    close("echo foobar")

    # Lê uma linha de um arquivo e armazena em localvar
    getline localvar <"/tmp/foobar.txt"
    close("/tmp/foobar.txt")
}

# Como dito no início, os programas AWK consistem de uma coleção de padrões
# e ações. Você já viu o padrão BEGIN, o mais importante. Outros padrões são
# usados apenas se você estiver processando linhas de arquivos ou a entrada
# padrão.

# Quando você passa argumentos para o AWK, eles são tratados como nomes de
# arquivos para processar. Todos serão processados, em ordem. Pense nisso como
# um implícito para loop, iterando sobre as linhas nesses arquivos. Esses
# padrões e ações são como instruções de mudança dentro do loop.

/^fo+bar$/ {

    # Esta ação será executada para cada linha que corresponda à expressão
    # regular, / ^ fo + bar $ /, e será ignorada para qualquer linha que não
    # corresponda. Vamos apenas imprimir a linha:

    print

    # Opa, sem argumento! Isso ocorre pois print tem um argumento padrão: $0.
    # $0 é o nome da linha atual que está sendo processada. Essa variável é
    # criada automaticamente para você.

    # Você provavelmente pode adivinhar que existem outras variáveis $. Toda
    # linha é implicitamente dividida antes de cada ação ser chamada, como
    # o shell faz. E, como o shell, cada campo pode ser acessado com um sinal
    # de cifrão

    # Isso irá imprimir o segundo e quarto campos da linha
    print $2, $4

    # O AWK automaticamente define muitas outras variáveis para ajudar você
    # a inspecionar processar cada linha. A mais importante delas é NF.

    # Imprime o número de campos da linha atual
    print NF

    # Imprime o último campo da linha atual
    print $NF
}

# Todo padrão é na verdade um teste verdadeiro/falso. A expressão regular no
# último padrão também é um teste verdadeiro/falso, mas parte dele estava
# escondido. Se você não informar um texto para testar, AWK assumirá $0,
# a linha que está atualmente sendo processada. Assim, a versão completa
# é a seguinte:

$0 ~ /^fo+bar$/ {
    print "Equivalente ao último padrão"
}

a > 0 {
    # Isso será executado uma vez para cada linha, quando a for positivo
}

# Você entendeu. Processar arquivos de texto, ler uma linha de cada vez, e
# fazer algo com ela, particularmente dividir com base em um delimitador, é
# tão comum no UNIX que AWK é uma linguagem de script que faz tudo por você,
# sem você precisa perguntar. Tudo o que você precisa fazer é escrever os
# padrões e ações com base no que você espera da entrada, e o que você quer
# fazer com isso.

# Aqui está um exemplo rápido de um script simples, o tipo de coisa que o AWK
# é perfeito para fazer. Ele irá ler um nome da entrada padrão e depois
imprimirá a média de idade de todos com esse primeiro nome. Digamos que você
forneça como argumento o nome de um arquivo com esses dados:

# Bob Jones 32
# Jane Doe 22
# Steve Stevens 83
# Bob Smith 29
# Bob Barker 72
#
# Eis o script:

BEGIN {

    # Primeiro, pergunte o nome do usuário
    print "Para qual nome você quer calcular a média de idade?"

    # Pega uma linha da entrada padrão, não dos arquivos indicados na
    # linha de comando
    getline name <"/dev/stdin"
}

# Agora, processa cada linha em que o primeiro nome é o nome informado
$1 == name {

    # Dentro desse bloco, nós temos acesso a algumas variáveis uteis, que
    # foram pré-carregadas para nós:
    # $0  é a linha corrente completa
    # $3 é o terceiro campo, que é o que nos interessa aqui
    # NF é a quantidade de campos, que deve ser 3
    # NR é o número de registros (linhas) lidas até agora
    # FILENAME é o nome do arquivo sendo processado
    # FS é o delimitador em uso, que é " " aqui
    # ...etc. Há muito mais, documentadas no manual.

    # Mantenha um registro do total e da quantidade de linhas encontradas
    sum += $3
    nlines++
}

# Outro padrão especial é chamado END. Ele será executado após o processamento
# de todos os arquivos de texto. Ao contrário de BEGIN, ele só será executado
# se você tiver dado a ele dados para processar. Ele será executado depois de
# todos os arquivos terem sido lidos e processados de acordo com as regras e
# ações que você forneceu. O objetivo disso em geral é produzir algum tipo de
# relatório final, ou fazer algo com o agregado dos dados acumulados ao longo
# do script.

END {
    if (nlines)
        print "A média da idade para " name " é " sum / nlines
}

Leituras adicionais (em inglês):


Sugestões ou correções? Abra uma issue no repositório do Github, ou faça um Pull Request você mesmo!

Originalmente contribuído por Marshall Mason e atualizado por 0 colaborador(es).