Introdução
Versões mais novas do bash (>= 4.x) suportam um novo tipo de array: o array associativo. Esse tipo de array, também chamado de hash em outras linguagens, consegue indexar seus elementos usando strings ao invés dos usuais números inteiros. Os usos desse tipo de array são os mais variados possíveis, como veremos nesse post.
Hoje iremos aprender como criar e manipular arrays associativos no bash.
Meu bash é >= 4.x ?
Antes de tudo você deve se assegurar que está usando uma versão >= 4.x do bash. Para isso há dois modos:
$ echo $BASH_VERSION 4.2.10(1)-release $ bash --version | head -n1 GNU bash, version 4.2.10(1)-release (i386-redhat-linux-gnu)
Declarando um array associativo
$ declare -A animais
O uso do declare é obrigatório para arrays associativos [1], portanto nunca esqueça de utilizá-lo. Após o parâmetro “-A” vem o nome do array associativo.
Bem, após declarar o array, você já pode associar valores a ele.
Usando o operador de igualdade:
animais=( [miau]=gato [auau]=cachorro [muu]=vaca )
Repare que dentro dos colchetes aparece a chave e após o ‘=’ vem o valor. Outra observação também é que se o valor contém espaços em branco, use aspas para englobá-lo como um só parâmetro.
É possível também assinalar um par chave/valor de cada vez:
animais[miau]=gato animais[auau]=cachorro animais[muu]=vaca
Obtendo chaves e valores
1) Obtendo as chaves
$ echo ${!animais[@]} miau auau muu
2) Obtendo os valores das chaves
$ echo ${animais[@]} gato cachorro vaca
OBS: Nesses casos as chaves e os valores vieram conforme a ordem que eles foram declarados, mas nem sempre é assim. Internamente as chaves e valores são ordenados seguindo um critério específico do algoritmo de hash e por causa disso a saída pode sair fora de ordem.
E por fim, para recuperar um valor de determinada chave, faça:
$ echo ${animais[auau]} cachorro
Quantidade de elementos
Assim como arrays normais, você pode recuperar a quantidade de elementos de um hash, assim:
$ echo ${#animais[@]} 6
Percorrendo um array associativo
Podemos usar um loop para percorrer todos os elementos de um array associativo. Para isso expandimos a lista de chaves e de posse de cada chave recuperamos seu valor. Exemplo:
$ for chave in ${!animais[@]}; do echo "$chave = ${animais[$chave]}"; done miau = gato auau = cachorro muu = vaca
Quando usamos uma variável como índice, obrigatoriamente devemos usar o cifrão [1].
Até que enfim os exemplos …
1) Contador de palavras
Esse é um exemplo clássico do uso de hashes. O que iremos fazer é pegar cada palavra de um texto e armazená-la como chave do hash, e no início seu valor será nulo (que é o mesmo que zero). Quando houver colisão, ou seja, se uma palavra (= chave) aparecer mais de uma vez, o seu valor será incrementado. No fim do script a quantidade de vezes em que cada palavra aparece no texto é mostrada.
$ cat doces.txt O doce perguntou pro doce Qual é o doce mais doce Que o doce de batata-doce O doce respondeu pro doce Que o doce mais doce que O doce de batata-doce É o doce de doce de batata-doce
O script:
#!/bin/bash #[contapalavra.sh] # cria o hash declare -A palavras # arquivo que contem o texto textofile='doce.txt' # percorre cada linha while read LINHA do # percorre cada palavra for PALAVRA in $LINHA do # incrementa o valor let palavras[$PALAVRA]++ done done < $textofile # Mostra as palavras e a # quantidade de vezes em que elas # apareceram for chave in ${!palavras[@]} do tput bold; echo -n "$chave"; tput sgr0 echo -n " apareceu " tput bold; echo -n ${palavras[$chave]} ; tput sgr0 echo " vez(es)." done
Executando-o temos:
$ ./contapalavra.sh mais apareceu 2 vez(es). respondeu apareceu 1 vez(es). que apareceu 1 vez(es). É apareceu 1 vez(es). de apareceu 4 vez(es). perguntou apareceu 1 vez(es). doce apareceu 12 vez(es). O apareceu 3 vez(es). Que apareceu 2 vez(es). é apareceu 1 vez(es). pro apareceu 2 vez(es). o apareceu 4 vez(es). batata-doce apareceu 3 vez(es). Qual apareceu 1 vez(es).
2) Quantos usuários usam determinado shell?
Esse script vasculha o arquivo /etc/passwd e conta quantos usuários usam determinado shell.
#!/bin/bash #[contashell.sh] # cria o hash declare -A shells # Obtem uma lista de shells for shell in $(cut -d: -f 7 /etc/passwd) do let shells[$shell]++ done # Mostra os valores do hash for chave in ${!shells[@]} do echo "$chave = ${shells[$chave]}" done
Executando:
$ ./contashell.sh /sbin/nologin = 40 /bin/sync = 1 /bin/bash = 3 /bin/sh = 1 /sbin/shutdown = 1 /sbin/halt = 1
3) Agrupar chaves pelo valor
Um array associativo não permite duplicação de chaves, mas nada impede que os valores sejam repetidos. Assim é possível agrupar chaves com valores semelhantes para resolver diversos tipos de problemas. Em [1] você encontrará algo semelhante aonde o autor agrupa animais “selvagens” e “domésticos” e guarda as respectivas chaves em vetores separados.
Conclusão
A possibilidade de usarmos strings como índices facilita muito a programação, principalmente nos casos em que queremos apagar ou contar as multiplicidades de palavras de determinada string, como vimos nos 3 exemplos apresentados. Infelizmente somente as versões mais novas do bash possibilitam o uso de arrays associativos, sobrando para aqueles que usam versões antigas o uso de “wrappers” [2] ou atualização para a versão mais nova do interpretador.
Referências
[1] Vetores associativos, by Júlio Cezar Neves (Acessado em: Março/2012)
http://www.dicas-l.com.br/cantinhodoshell/cantinhodoshell_20100120.php
[2] How to define hash tables in bash? from http://stackoverflow.com (Acessado em: Março/2012)
http://stackoverflow.com/questions/1494178/how-to-define-hash-tables-in-bash
[3] Trava Língua (Acessado em: Março/2012)
http://www.alzirazulmira.com/trava.htm
[4] Como utilizar arrays e arrays associativos em shell script by Gustavo Dutra (Acessado em: Março/2012)
http://gustavodutra.com/geek/como-utilizar-arrays-e-arrays-associativos-em-shell-script