Arrays no Bash: Arrays Indexados e Associativos

Arrays no Bash: Arrays Indexados e Associativos

O Que Você Vai Aprender

  • A diferença entre arrays indexados e arrays associativos no bash
  • As operações fundamentais: declarar, acessar, iterar e adicionar/remover elementos
  • Como evitar armadilhas clássicas como aspas faltantes em "${arr[@]}" e arrays esparsos

Resumo Rápido

  • Lista ordenada de valores -> array indexado (arr=(a b c))
  • Tabela de busca chave/valor -> array associativo (declare -A map é obrigatório)
  • Sempre use aspas duplas em "${arr[@]}" ao iterar ou expandir

Pré-requisitos (ambiente alvo)

  • Shell: bash 4.0+ (arrays associativos precisam de 4.0+, índices negativos precisam de 4.3+)
  • sh / dash não suportam arrays. Use um shebang explícito #!/bin/bash

O que é um array?

Conclusão: Um array armazena múltiplos valores em uma única variável. O bash tem dois tipos: arrays indexados ordenados e arrays associativos baseados em chaves.

Uma variável de shell simples armazena um único valor. Quando você precisa lidar com vários nomes de arquivos ou usuários juntos, colocar tudo em uma string separada por espaços quebra assim que um valor contiver um espaço. Um array mantém cada elemento como um valor independente e seguramente entre aspas.

O bash oferece dois tipos de array:

  • Array indexado: elementos são acessados por índices inteiros começando em 0
  • Array associativo: elementos são buscados por uma chave de string arbitrária (como um hash ou dicionário)

Como usar arrays indexados?

Conclusão: Declare com arr=(a b c), leia um elemento com ${arr[0]} e todos os elementos com "${arr[@]}". Omitir o índice refere-se ao primeiro elemento.

Declarando e atribuindo

# Declarar tudo de uma vez
fruits=(apple banana cherry)

# Atribuir por indice
fruits[3]=durian

# Adicionar ao final (operador +=)
fruits+=(elderberry fig)

Acessando

echo "${fruits[0]}"      # apple (primeiro elemento)
echo "${fruits[-1]}"     # fig (ultimo, bash 4.3+)
echo "${fruits[@]}"      # todos os elementos, separados por espaco
echo "${#fruits[@]}"     # quantidade de elementos (6)
echo "${!fruits[@]}"     # todos os indices (0 1 2 3 4 5)

Referenciar um array sem índice, como $fruits, é o mesmo que ${fruits[0]} (apenas o primeiro elemento). Cuidado com essa confusão quando você pretendia usar todos os elementos.

Fatiamento (extraindo um subconjunto)

echo "${fruits[@]:1:2}"  # banana cherry (2 elementos a partir do indice 1)

Como usar arrays associativos?

Conclusão: Sempre declare um array associativo com declare -A antes de usar. Pule isso e o bash o trata como array indexado, colapsando chaves para 0 e corrompendo os dados.

Um array associativo permite usar strings arbitrárias como chaves. A diferença principal dos arrays indexados é que você deve declará-lo com declare -A primeiro.

declare -A capital          # esquecer isso e ele quebra
capital[japan]=Tokyo
capital[france]=Paris
capital["united states"]=Washington

echo "${capital[japan]}"    # Tokyo
echo "${!capital[@]}"       # todas as chaves (ordem indefinida)
echo "${capital[@]}"        # todos os valores (ordem indefinida)
echo "${#capital[@]}"       # quantidade de elementos (3)

Arrays associativos não preservam ordem. Nem a ordem de inserção nem a ordem das chaves é garantida na saída, então use pipe com sort se precisar de ordenação.

Como iterar sobre um array?

Conclusão: Itere valores com for x in "${arr[@]}" e chaves/índices com for k in "${!arr[@]}". Ambos exigem aspas duplas.

# Processar cada valor por vez
for f in "${fruits[@]}"; do
    echo "fruta: $f"
done

# Iterar chaves (associativo) e buscar valores
for country in "${!capital[@]}"; do
    echo "$country -> ${capital[$country]}"
done

Remover as aspas, como em for f in ${fruits[@]}, causa divisão de palavras em elementos que contêm espaços: um único elemento apple pie se divide em apple e pie. Sempre use "${arr[@]}" para manter os valores intactos.

Qual é a diferença entre "${arr[@]}" e "${arr[*]}"?

Conclusão: [@] expande cada elemento como uma palavra separada; [*] junta todos em uma única string usando o primeiro caractere de IFS. Use [@] para loops.

Notação Expande para Uso típico
"${arr[@]}" uma palavra separada por elemento loops, passagem de args
"${arr[*]}" uma única string unida por IFS[0] exibição, stringify
arr=(one two three)

printf '[%s]\n' "${arr[@]}"   # [one] [two] [three] em linhas separadas
printf '[%s]\n' "${arr[*]}"   # [one two three] em uma linha

# Unir com um separador personalizado
IFS=,
echo "${arr[*]}"              # one,two,three
unset IFS

Como adicionar ou remover elementos?

Conclusão: Adicione com arr+=(...) e exclua com unset 'arr[2]'. Note que remover de um array indexado o deixa esparso - os índices não são renumerados.

arr=(a b c d)

arr+=(e)            # adicionar -> a b c d e
unset 'arr[1]'      # excluir indice 1 (b)

echo "${arr[@]}"    # a c d e
echo "${!arr[@]}"   # 0 2 3 4 <- indice 1 esta faltando (nao renumerado)

Após unset, o array não é mais contíguo (array esparso). A contagem ${#arr[@]} não corresponde mais ao maior índice, então qualquer loop assumindo índices 0..N-1 quebra. Reatribua para renumerar:

arr=("${arr[@]}")   # reindexar para 0,1,2...

Quais são as armadilhas comuns?

Conclusão: Arrays são exclusivos do bash, aspas são obrigatórias, e arrays associativos precisam de declare -A. Perca qualquer um desses e você terá uma falha clássica.

Para capturar saída de comandos em um array, mapfile (também chamado readarray) lê com segurança linha por linha.

mapfile -t lines < <(ls -1)   # cada linha se torna um elemento de lines
echo "${#lines[@]}"           # numero de linhas

A opção -t remove a nova linha final de cada linha; sem ela, os elementos mantêm um \n.

Resumo

  • Arrays indexados arr=(a b c) são ordenados; arrays associativos declare -A map são baseados em chaves
  • Todos os elementos: "${arr[@]}", contagem: ${#arr[@]}, índices/chaves: ${!arr[@]}
  • Sempre use aspas duplas em loops e expansões para evitar acidentes de divisão de palavras
  • Arrays associativos exigem declare -A, e arrays ficam esparsos após unset

Próximas leituras: