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 mapfile para 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

Exercícios

  1. Declare um array chamado planetas com os nomes: Mercúrio, Vênus, Terra, Marte. Imprima o terceiro elemento e o número total de elementos.

✓ Resposta:
planetas=("Mercúrio" "Vênus" "Terra" "Marte")
echo "Terceiro planeta: ${planetas[2]}"
echo "Total de planetas: ${#planetas[@]}"
  • Escreva um script que itere sobre o array frutas (maçã, banana, laranja) e imprima cada fruta em uma linha, precedida por um número de ordem (1, 2, 3).
  • ✓ Resposta:
    frutas=("maçã" "banana" "laranja")
    contador=1
    for fruta in "${frutas[@]}"; do
        echo "$contador: $fruta"
        ((contador++))
    done
  • Dado o array nomes=("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.
  • ✓ Resposta:
    nomes=("Ana Maria" "João Pedro" "Maria José")
    for nome in "${nomes[@]}"; do
        echo "$nome"
    done
  • Crie um array com os números de 1 a 5 usando uma sequência (ex.: {1..5}) e calcule a soma de todos os elementos, imprimindo o resultado.
  • ✓ Resposta:
    numeros=({1..5})
    soma=0
    for n in "${numeros[@]}"; do
        soma=$((soma + n))
    done
    echo "Soma: $soma"
  • Escreva um script que leia o conteúdo de um arquivo chamado lista.txt para um array usando mapfile e depois imprima cada linha com seu índice (começando de 0).
  • ✓ Resposta:
    mapfile -t linhas < lista.txt
    for i in "${!linhas[@]}"; do
        echo "$i: ${linhas[$i]}"
    done