Arrays no Bash: Arrays Indexados e Associativos

Arrays no Bash: Arrays Indexados e Associativos

O Que Voce Vai Aprender

  • A diferenca entre arrays indexados e arrays associativos no bash
  • As operacoes fundamentais: declarar, acessar, iterar e adicionar/remover elementos
  • Como evitar armadilhas classicas como aspas faltantes em "${arr[@]}" e arrays esparsos

Resumo Rapido

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

Pre-requisitos (ambiente alvo)

  • Shell: bash 4.0+ (arrays associativos precisam de 4.0+, indices negativos precisam de 4.3+)
  • sh / dash nao suportam arrays. Use um shebang explicito #!/bin/bash

O que e um array?

Conclusao: Um array armazena multiplos valores em uma unica variavel. O bash tem dois tipos: arrays indexados ordenados e arrays associativos baseados em chaves.

Uma variavel de shell simples armazena um unico valor. Quando voce precisa lidar com varios nomes de arquivos ou usuarios juntos, colocar tudo em uma string separada por espacos quebra assim que um valor contiver um espaco. Um array mantém cada elemento como um valor independente e seguramente entre aspas.

O bash oferece dois tipos de array:

  • Array indexado: elementos sao acessados por indices inteiros comecando em 0
  • Array associativo: elementos sao buscados por uma chave de string arbitraria (como um hash ou dicionario)

Como usar arrays indexados?

Conclusao: Declare com arr=(a b c), leia um elemento com ${arr[0]} e todos os elementos com "${arr[@]}". Omitir o indice 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 indice, como $fruits, e o mesmo que ${fruits[0]} (apenas o primeiro elemento). Cuidado com essa confusao quando voce 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?

Conclusao: 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 arbitrarias como chaves. A diferenca principal dos arrays indexados e que voce deve declara-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 nao preservam ordem. Nem a ordem de insercao nem a ordem das chaves e garantida na saida, entao use pipe com sort se precisar de ordenacao.

Como iterar sobre um array?

Conclusao: Itere valores com for x in "${arr[@]}" e chaves/indices 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 divisao de palavras em elementos que contem espacos: um unico elemento apple pie se divide em apple e pie. Sempre use "${arr[@]}" para manter os valores intactos.

Qual e a diferenca entre "${arr[@]}" e "${arr[*]}"?

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

Notacao Expande para Uso tipico
"${arr[@]}" uma palavra separada por elemento loops, passagem de args
"${arr[*]}" uma unica string unida por IFS[0] exibicao, 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?

Conclusao: Adicione com arr+=(...) e exclua com unset 'arr[2]'. Note que remover de um array indexado o deixa esparso - os indices nao sao 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)

Apos unset, o array nao e mais contíguo (array esparso). A contagem ${#arr[@]} nao corresponde mais ao maior indice, entao qualquer loop assumindo indices 0..N-1 quebra. Reatribua para renumerar:

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

Quais sao as armadilhas comuns?

Conclusao: Arrays sao exclusivos do bash, aspas sao obrigatorias, e arrays associativos precisam de declare -A. Perca qualquer um desses e voce tera uma falha classica.

Para capturar saida de comandos em um array, mapfile (tambem chamado readarray) le com seguranca linha por linha.

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

A opcao -t remove a nova linha final de cada linha; sem ela, os elementos mantem um \n.

Resumo

  • Arrays indexados arr=(a b c) sao ordenados; arrays associativos declare -A map sao baseados em chaves
  • Todos os elementos: "${arr[@]}", contagem: ${#arr[@]}, indices/chaves: ${!arr[@]}
  • Sempre use aspas duplas em loops e expansoes para evitar acidentes de divisao de palavras
  • Arrays associativos exigem declare -A, e arrays ficam esparsos apos unset

Proximas leituras: