Arrays no Bash
LidoEsta aula ensina como trabalhar com arrays no Bash, desde a declaração e acesso a elementos até a iteração sobre todos os itens. Você verá exemplos práticos e exercícios para fixar o conteúdo.
Arrays são estruturas de dados fundamentais em qualquer linguagem de programação, e no Bash não é diferente. Eles permitem armazenar múltiplos valores em uma única variável, facilitando o processamento de listas, coleções de arquivos, parâmetros e muito mais. Nesta aula, você vai aprender a declarar arrays, acessar elementos individuais, obter todos os elementos de uma vez e percorrê-los com loops.
Dominar arrays no Bash é essencial para escrever scripts mais eficientes e elegantes. Em vez de criar dezenas de variáveis (arquivo1, arquivo2, ...), você pode usar um array e manipular todos os dados com loops e expansões. Vamos mergulhar nos detalhes.
Declaração
Em Bash, existem duas formas principais de declarar um array: explicitamente com a sintaxe nome=(elemento1 elemento2 ...) ou usando a flag -a com o comando declare. A primeira é a mais comum e direta. Você pode declarar um array vazio com nome=() e adicionar elementos depois.
Os elementos podem ser strings, números ou até mesmo resultados de comandos. Se você quiser incluir espaços em um elemento, é necessário usar aspas. Vejamos alguns exemplos:
# Declaração simples
frutas=("maçã" "banana" "laranja")
# Array de números
numeros=(10 20 30 40)
# Array vazio
vazio=()
# Declarando com declare -a (explícito)
declare -a cores=("vermelho" "verde" "azul")
# Adicionando elementos após a declaração
vazio[0]="primeiro"
vazio[1]="segundo"
# Array com resultado de comando
arquivos=($(ls *.txt)) # cuidado: quebra por espaços; melhor usar mapfile ou IFS
É importante notar que, em Bash, os arrays são indexados a partir de zero, como na maioria das linguagens. O tamanho do array não precisa ser definido previamente; ele cresce dinamicamente.
Acesso e índices
Para acessar um elemento específico de um array, usamos a sintaxe ${array[índice]}. Os índices podem ser números ou expressões aritméticas. Se você tentar acessar um índice que não existe, o Bash retornará uma string vazia (sem erro).
Além de acessar elementos individuais, você pode modificar um elemento atribuindo um novo valor ao índice correspondente. Para obter o número total de elementos, use ${#array[@]}. O último índice pode ser obtido com ${#array[@]} - 1.
#!/bin/bash
frutas=("maçã" "banana" "laranja")
# Acessando elementos
echo "Primeira fruta: ${frutas[0]}"
echo "Segunda fruta: ${frutas[1]}"
# Modificando um elemento
frutas[1]="manga"
echo "Fruta alterada: ${frutas[1]}"
# Número de elementos
echo "Total de frutas: ${#frutas[@]}"
# Acessando o último elemento (índice = tamanho - 1)
ultimo=$(( ${#frutas[@]} - 1 ))
echo "Última fruta: ${frutas[$ultimo]}"
# Tentativa de acessar índice inexistente
unset frutas[5] # não faz nada, pois não existe
echo "Índice 5: '${frutas[5]}' (vazio)"
Você também pode remover um elemento com unset, mas isso não reindexa o array; ele fica com 'buracos'. Para verificar se um índice existe, é possível usar [[ -v array[índice] ]] no Bash 4.2+.
${array[@]}
A expansão ${array[@]} é uma das mais importantes. Ela expande para todos os elementos do array, cada um como uma palavra separada. Isso é útil para passar todos os elementos como argumentos para um comando ou para iterar sobre eles. Difere de ${array[*]}, que expande para uma única palavra com os elementos separados pelo primeiro caractere do IFS (geralmente espaço).
Quando você usa ${array[@]} entre aspas duplas ("${array[@]}"), cada elemento é tratado como uma palavra separada, mesmo que contenha espaços. Isso é crucial para evitar problemas com nomes de arquivos que contenham espaços.
#!/bin/bash
nomes=("João Silva" "Maria Souza" "Pedro Santos")
# Exibindo todos os elementos
echo "Usando \${array[@]}: ${nomes[@]}"
# Iterando com for (cada elemento é uma palavra)
for pessoa in "${nomes[@]}"; do
echo "Nome: $pessoa"
done
# Passando como argumentos para um comando
touch "${nomes[@]}" # criaria arquivos com esses nomes (cuidado!)
# Diferença com ${array[*]}
echo "Usando \${array[*]}: ${nomes[*]}"
No exemplo acima, se você usasse ${nomes[@]} sem aspas, os nomes com espaços seriam quebrados em várias palavras, causando erros. Por isso, a prática recomendada é sempre usar "${array[@]}" quando quiser preservar cada elemento como uma unidade.
Iteração
A forma mais comum de percorrer um array é com o loop for. Você pode iterar sobre os valores diretamente ou sobre os índices. Vamos ver as duas abordagens.
Iterar sobre os valores é mais simples e legível. Basta usar for elemento in "${array[@]}". Se você precisar dos índices, pode iterar sobre a sequência de 0 a tamanho-1, ou usar a expansão ${!array[@]} que retorna os índices do array (útil quando há buracos).
#!/bin/bash
cores=("vermelho" "verde" "azul")
# Iterando sobre os valores
echo "Valores:"
for cor in "${cores[@]}"; do
echo " - $cor"
done
# Iterando sobre os índices
echo "Índices:"
for i in "${!cores[@]}"; do
echo " Índice $i: ${cores[$i]}"
done
# Usando um contador manual (clássico)
echo "Com contador:"
for (( i=0; i<${#cores[@]}; i++ )); do
echo " $i: ${cores[$i]}"
done
Além do for, você pode usar while com um contador, mas o for é mais idiomático. Também é possível usar o comando mapfile (ou readarray) para ler linhas de um arquivo diretamente em um array, o que é muito útil para processar arquivos linha por linha.
# Lendo linhas de um arquivo para um array
mapfile -t linhas < arquivo.txt
for linha in "${linhas[@]}"; do
echo "Linha: $linha"
done
Boas práticas e observações
Ao trabalhar com arrays no Bash, algumas práticas ajudam a evitar erros:
- Sempre use aspas duplas ao expandir arrays, especialmente com
${array[@]}, para preservar elementos com espaços. - Prefira
mapfilepara ler arquivos, pois ele lida corretamente com espaços e caracteres especiais. - Lembre-se de que arrays em Bash são 0-indexados.
- Use
${#array[@]}para obter o tamanho, não${#array}(que daria o tamanho do primeiro elemento). - Para remover um elemento, use
unset 'array[índice]'(com aspas para evitar expansão indesejada). - Se precisar de arrays associativos (com chaves nomeadas), use
declare -A(Bash 4+).
Referências
- Bash Reference Manual - Arrays
- Bash cheatsheet (devhints.io)
- BashGuide - Arrays
- ShellCheck - analisador de scripts
- Bash Conditional Expressions (para -v)
- Shell Parameter Expansion
Exercícios
- Declare um array chamado
planetascom os nomes: Mercúrio, Vênus, Terra, Marte. Imprima o terceiro elemento e o número total de elementos.
planetas=("Mercúrio" "Vênus" "Terra" "Marte")
echo "Terceiro planeta: ${planetas[2]}"
echo "Total de planetas: ${#planetas[@]}"frutas (maçã, banana, laranja) e imprima cada fruta em uma linha, precedida por um número de ordem (1, 2, 3).frutas=("maçã" "banana" "laranja")
contador=1
for fruta in "${frutas[@]}"; do
echo "$contador: $fruta"
((contador++))
donenomes=("Ana Maria" "João Pedro" "Maria José"), use um loop for para imprimir cada nome completo em uma linha separada, sem quebrar os nomes com espaços.nomes=("Ana Maria" "João Pedro" "Maria José")
for nome in "${nomes[@]}"; do
echo "$nome"
donenumeros=({1..5})
soma=0
for n in "${numeros[@]}"; do
soma=$((soma + n))
done
echo "Soma: $soma"lista.txt para um array usando mapfile e depois imprima cada linha com seu índice (começando de 0).mapfile -t linhas < lista.txt
for i in "${!linhas[@]}"; do
echo "$i: ${linhas[$i]}"
done