0Pricing
Linux Command Line & Bash Scripting Mastery · Aula

Passagem de matrizes e mapas associativos entre funções

Use referências nominais e expansão indireta para passar estruturas de dados complexas para dentro e para fora das funções com segurança.

Passagem de matrizes e mapas associativos entre funções é uma aula grátis de Linux Command Line & Bash Scripting Mastery no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Linux Command Line & Bash Scripting Mastery, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Linux Command Line & Bash Scripting Mastery inclui 4 aulas no total.

Por que é complicado passar matrizes para funções

No Bash, matrizes e matrizes associativas não são valores de primeira classe. Ao tentar passar uma matriz para uma função, não é possível simplesmente usar $my_array — isso expande apenas o primeiro elemento.

Considere esta tentativa incorreta:

my_func "${my_array[@]}"

A função recebe uma lista simples de palavras. Ela não tem como saber onde termina um elemento e começa o próximo, e perde completamente a estrutura da matriz.

O Bash oferece dois mecanismos confiáveis para passar estruturas de dados complexas:

  • Referências por nome (declare -n) — uma referência a outra variável pelo nome (Bash 4.3+)
  • Expansão indireta (${!varname[@]}) — acesso a uma variável cujo nome está armazenado em outra variável (versões mais antigas do Bash)

Esta lição aborda as duas técnicas para que possa escrever funções robustas e reutilizáveis que funcionem com matrizes e mapas.

Passando uma matriz pelo nome usando uma referência por nome

A abordagem moderna mais limpa é a referência por nome: passa-se o nome da matriz como uma cadeia de caracteres e, dentro da função, declara-se uma variável local que faz referência a ela.

Sintaxe principal:

  • declare -n ref="$1" — ref passa a ser um alias para qualquer nome de variável que tenha sido passado como primeiro argumento
  • Pode ler, percorrer e modificar ref exatamente como se fosse a matriz original

Isso evita copiar a matriz inteira e permite que a função trabalhe com matrizes de qualquer tamanho.

#!/usr/bin/env bash

print_array() {
    declare -n arr="$1"   # nameref: arr -> caller's variable
    echo "Array has ${#arr[@]} elements:"
    for elem in "${arr[@]}"; do
        echo "  - $elem"
    done
}

fruits=(apple banana cherry)
print_array fruits

Modificando a matriz de quem chamou dentro de uma função

Como uma referência por nome é um alias verdadeiro, as gravações feitas por meio dela alteram a variável original de quem chamou. Não é necessário devolver valores com echo nem usar variáveis globais.

Isso torna as referências por nome ideais para funções que transformam matrizes no próprio lugar — por exemplo, normalizando cadeias de caracteres, filtrando elementos ou ordenando-os.

#!/usr/bin/env bash

to_uppercase() {
    declare -n _arr="$1"
    for i in "${!_arr[@]}"; do
        _arr[$i]="${_arr[$i]^^}"   # ^^ = uppercase in Bash 4+
    done
}

words=(hello world bash)
echo "Before: ${words[@]}"
to_uppercase words
echo "After:  ${words[@]}"

Retornando uma nova matriz por meio de uma referência por nome

Uma função também pode escrever em uma matriz fornecida por quem a chamou, aceitando o nome dela como parâmetro de saída. Quem chama cria uma matriz vazia, passa seu nome e a função a preenche.

Esse padrão lembra o idioma clássico de C de passar um ponteiro para um buffer de resultado — exceto pelo fato de que aqui tudo é feito no shell.

#!/usr/bin/env bash

get_even_numbers() {
    local -a source=("$@")
    # Last argument is the output array name — pop it off
    local out_name="${source[-1]}"
    unset 'source[-1]'

    declare -n _out="$out_name"
    _out=()   # clear
    for n in "${source[@]}"; do
        (( n % 2 == 0 )) && _out+=("$n")
    done
}

declare -a evens
get_even_numbers 1 2 3 4 5 6 7 8 evens
echo "Evens: ${evens[@]}"

A armadilha da colisão de referências por nome

Há uma armadilha crítica com referências por nome: se a variável local de referência por nome tiver o mesmo nome que a variável de quem chamou, o Bash entra em um ciclo infinito de autorreferência e o script falha.

Por exemplo, se quem chama der à matriz o nome arr e a função fizer declare -n arr="$1", a referência por nome arr apontará para si mesma.

Convenção para evitar isso: prefixe as variáveis internas de referência por nome com um sublinhado (por exemplo, _arr, _ref, _out). Quase nunca quem chama usa esses nomes, o que torna as colisões extremamente raras.

#!/usr/bin/env bash

# DANGER: caller uses variable named 'arr'
arr=(one two three)

bad_func() {
    declare -n arr="$1"   # collision! arr refers to itself
    echo "${arr[@]}"
}

good_func() {
    declare -n _arr="$1"  # underscore prefix avoids collision
    echo "${_arr[@]}"
}

# bad_func arr   # would hang / error
good_func arr

Passando matrizes associativas (mapas) pelo nome

As matrizes associativas (declare -A) funcionam da mesma forma com referências por nome. Passe o nome do mapa como uma cadeia de caracteres e declare uma referência por nome dentro da função — terá acesso completo de leitura e gravação a todos os pares de chave e valor.

Isso é muito útil para mapas de configuração, analisadores de opções e tabelas de consulta que precisam ser compartilhados entre funções de biblioteca.

#!/usr/bin/env bash

print_map() {
    declare -n _map="$1"
    echo "Map contents:"
    for key in "${!_map[@]}"; do
        printf '  %-15s => %s\n' "$key" "${_map[$key]}"
    done
}

declare -A config
config[host]="localhost"
config[port]="5432"
config[db]="myapp"

print_map config

Mesclando dois mapas com uma referência por nome

Como as referências por nome fornecem à função um alias ativo, é possível implementar utilitários como a mesclagem de mapas de forma limpa. Passe o mapa de destino e um ou mais mapas de origem pelo nome; a função percorre as chaves de origem e as grava no destino.

As referências por nome permitem escrever uma única função genérica merge_map que funciona com quaisquer matrizes associativas no seu script.

#!/usr/bin/env bash

merge_map() {
    # merge_map dest src
    declare -n _dest="$1"
    declare -n _src="$2"
    for key in "${!_src[@]}"; do
        _dest["$key"]="${_src[$key]}"
    done
}

declare -A defaults=([timeout]=30 [retries]=3 [verbose]=false)
declare -A overrides=([retries]=5 [log_level]=debug)

merge_map defaults overrides

for k in "${!defaults[@]}"; do
    echo "$k = ${defaults[$k]}"
done

Expansão indireta: compatibilidade com versões antigas do Bash

Se precisar oferecer suporte ao Bash 4.0–4.2 (ou aos shells padrão mais antigos do macOS), declare -n não estará disponível. A alternativa é a expansão indireta com ${!varname}.

A expansão indireta avalia o valor de varname como um nome de variável e retorna o valor dessa variável. Para matrizes, combine-a com uma sintaxe especial para obter todos os elementos.

Limitações em comparação com referências por nome:

  • Na prática, é somente para leitura (gravar de volta exige eval, que apresenta riscos de injeção)
  • As chaves de matrizes associativas não podem ser percorridas com segurança sem eval
  • Use referências por nome sempre que possível; recorra à expansão indireta somente quando necessário
#!/usr/bin/env bash

sum_array() {
    local arr_name="$1"
    # Indirect: build a temporary copy by expanding arr_name[@]
    local -a items
    eval "items=( \"\${${arr_name}[@]}\" )"
    local total=0
    for n in "${items[@]}"; do
        (( total += n ))
    done
    echo "$total"
}

numbers=(10 20 30 40)
result=$(sum_array numbers)
echo "Sum: $result"

Criando uma biblioteca de scripts modular

Em projetos profissionais de Bash, as funções utilitárias são armazenadas em arquivos de biblioteca (por exemplo, lib/array_utils.sh) e carregadas por scripts que precisam delas.

O padrão é:

  • Cada arquivo de biblioteca define apenas funções — sem efeitos colaterais no nível superior
  • Evite o carregamento duplo usando uma variável sentinela: [[ -n $__LIB_LOADED ]] && return; __LIB_LOADED=1
  • Os scripts consumidores usam source ou . para carregar a biblioteca
  • As funções trocam dados complexos por meio de referências por nome, mantendo toda a lógica autocontida

Isso produz scripts que podem ser testados, reutilizados e mantidos — não soluções improvisadas de shell feitas para uma única ocasião.

Exemplo prático: analisador de opções que retorna um mapa

Um caso de uso real: um analisador de opções de linha de comando que preenche uma matriz associativa fornecida por quem o chamou. Quem chama passa o nome da matriz como primeiro argumento, seguido pelos argumentos "$@" reais.

Esse padrão permite que qualquer script reutilize o mesmo analisador sem copiar código nem depender de variáveis globais.

#!/usr/bin/env bash

parse_opts() {
    declare -n _opts="$1"
    shift
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --host)   _opts[host]="$2";   shift 2 ;;
            --port)   _opts[port]="$2";   shift 2 ;;
            --user)   _opts[user]="$2";   shift 2 ;;
            *)        echo "Unknown option: $1" >&2; shift ;;
        esac
    done
}

declare -A opts=([host]=localhost [port]=5432 [user]=admin)
parse_opts opts --host db.example.com --port 3306

echo "Connecting to ${opts[host]}:${opts[port]} as ${opts[user]}"

Combinando referências por nome de entrada e saída em uma função

As funções podem manter várias referências por nome simultaneamente — uma para a entrada e outra para a saída. Isso permite funções de transformação puras: elas leem uma estrutura de origem, calculam um resultado e o gravam em uma estrutura de destino, tudo sem tocar no estado global.

Usar nomes distintos com prefixo de sublinhado para cada referência por nome elimina as colisões, mesmo quando as variáveis de quem chama têm nomes simples.

#!/usr/bin/env bash

filter_map_by_key_prefix() {
    # Usage: filter_map_by_key_prefix src_map dest_map prefix
    declare -n _src="$1"
    declare -n _dst="$2"
    local prefix="$3"

    _dst=()
    for key in "${!_src[@]}"; do
        if [[ $key == ${prefix}* ]]; then
            _dst["$key"]="${_src[$key]}"
        fi
    done
}

declare -A settings=(
    [db_host]=localhost [db_port]=5432
    [cache_ttl]=300     [cache_driver]=redis
    [app_debug]=true
)
declare -A db_settings

filter_map_by_key_prefix settings db_settings db_

for k in "${!db_settings[@]}"; do
    echo "$k = ${db_settings[$k]}"
done

Verificação de conhecimento: referências por nome e passagem de matrizes

Teste sua compreensão sobre a passagem de matrizes e mapas entre funções no Bash.

Recapitulação: passando estruturas de dados complexas no Bash

Eis o que aprendeu nesta lição:

  • Não é possível passar matrizes por valor — passe o nome da variável como uma cadeia de caracteres.
  • declare -n ref="$varname" cria uma referência por nome: um alias ativo para a variável de quem chamou. Tanto as leituras quanto as gravações são encaminhadas à variável original. Requer Bash 4.3+.
  • A colisão de referências por nome ocorre quando a variável interna e a externa têm o mesmo nome. Evite-a prefixando as referências por nome internas (por exemplo, _arr, _map).
  • Os parâmetros de saída funcionam passando o nome de uma matriz ou mapa de resultado vazio; a função o preenche por meio de uma referência por nome.
  • Várias referências por nome em uma função permitem transformações puras de entrada para saída, sem efeitos colaterais globais.
  • A expansão indireta (${!varname[@]} + eval) é uma alternativa antiga para Bash < 4.3, mas apresenta riscos de injeção e, na prática, é somente para leitura.
  • Combine essas técnicas com arquivos de biblioteca carregados para criar bases de código Bash modulares, reutilizáveis e testáveis.

Perguntas Frequentes

A aula “Passagem de matrizes e mapas associativos entre funções” é grátis?

Sim — o texto completo de “Passagem de matrizes e mapas associativos entre funções” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Linux Command Line & Bash Scripting Mastery, atualize para CoddyKit PRO. O curso de Linux Command Line & Bash Scripting Mastery inclui 4 aulas no total.

O que vou aprender em “Passagem de matrizes e mapas associativos entre funções”?

Use referências nominais e expansão indireta para passar estruturas de dados complexas para dentro e para fora das funções com segurança. Você pratica Linux Command Line & Bash Scripting Mastery com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Linux Command Line & Bash Scripting Mastery?

Nenhuma experiência prévia é necessária. Linux Command Line & Bash Scripting Mastery no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Passagem de matrizes e mapas associativos entre funções”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Linux Command Line & Bash Scripting Mastery?

Sim. Cada aula de Linux Command Line & Bash Scripting Mastery inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Projeto de funções com escopo local e códigos de retorno
  2. Construção e carregamento de bibliotecas Bash reutilizáveis
  3. Análise de opções e argumentos com getopts
  4. Passagem de matrizes e mapas associativos entre funções
← Voltar para Linux Command Line & Bash Scripting Mastery