--- category: tool tool: AWK filename: learnawk-pt.awk contributors: - ["Marshall Mason", "http://github.com/marshallmason"] translators: - ["Paulo Henrique Rodrigues Pinheiro", "https://github.com/paulohrpinheiro"] lang: pt-br --- 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. ```awk #!/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(b, a) # 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): * [Awk tutorial](http://www.grymoire.com/Unix/Awk.html) * [Awk man page](https://linux.die.net/man/1/awk) * [The GNU Awk User's Guide](https://www.gnu.org/software/gawk/manual/gawk.html) GNU AWK é encontrado na maioria dos sistemas GNU/Linux.