Linux Command Line & Bash Scripting Mastery · Leçon

Transmettre des tableaux et des cartes associatives entre fonctions

Utilisez les références nommées et l’expansion indirecte pour transmettre en toute sécurité des structures de données complexes aux fonctions et en récupérer les résultats.

Leçon 4 sur 413 étapes

Transmettre des tableaux et des cartes associatives entre fonctions est une leçon Linux Command Line & Bash Scripting Mastery gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Linux Command Line & Bash Scripting Mastery, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Linux Command Line & Bash Scripting Mastery comprend 4 leçons au total.

Pourquoi le passage de tableaux aux fonctions est délicat

Dans Bash, les tableaux et les tableaux associatifs ne sont pas des valeurs de première classe. Lorsque vous essayez de passer un tableau à une fonction, vous ne pouvez pas simplement utiliser $my_array : cela ne développe que le premier élément.

Considérez cette tentative incorrecte :

my_func "${my_array[@]}"

La fonction reçoit une liste plate de mots. Elle n'a aucun moyen de savoir où se termine un élément et où commence le suivant, et elle perd complètement la structure du tableau.

Bash fournit deux mécanismes fiables pour passer des structures de données complexes :

  • Références de nom (declare -n) — une référence à une autre variable par son nom (Bash 4.3+)
  • Développement indirect (${!varname[@]}) — accès à une variable dont le nom est stocké dans une autre variable (anciennes versions de Bash)

Cette leçon couvre les deux techniques afin que vous puissiez écrire des fonctions robustes et réutilisables qui fonctionnent avec des tableaux et des tables associatives.

Passer un tableau par son nom à l'aide d'une référence de nom

L'approche moderne la plus claire est la référence de nom : vous passez le nom du tableau sous forme de chaîne, puis, à l'intérieur de la fonction, vous déclarez une variable locale qui y fait référence.

Syntaxe essentielle :

  • declare -n ref="$1" — ref est désormais un alias du nom de variable passé comme premier argument
  • Vous lisez, parcourez et modifiez ref exactement comme s'il s'agissait du tableau d'origine

Cela évite de copier le tableau entier et permet à la fonction de travailler avec des tableaux de toute taille.

#!/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

Modifier le tableau de l'appelant à l'intérieur d'une fonction

Comme une référence de nom est un véritable alias, les écritures effectuées via la référence de nom modifient la variable d'origine de l'appelant. Il n'est pas nécessaire de renvoyer les valeurs avec echo ni d'utiliser des variables globales.

Les références de nom sont donc idéales pour les fonctions qui transforment les tableaux sur place, par exemple pour normaliser des chaînes, filtrer des éléments ou effectuer un tri.

#!/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[@]}"

Renvoyer un nouveau tableau via une référence de nom

Une fonction peut également écrire dans un tableau fourni par l'appelant en acceptant son nom comme paramètre de sortie. L'appelant crée un tableau vide, en passe le nom, puis la fonction le remplit.

Ce modèle rappelle l'idiome classique du langage C qui consiste à passer un pointeur vers un tampon de résultat, sauf qu'ici tout est réalisé dans le 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[@]}"

Le piège des collisions de références de nom

Les références de nom comportent un piège critique : si la variable locale de référence de nom porte le même nom que la variable de l'appelant, Bash entre dans une boucle infinie d'auto-référence et le script échoue.

Par exemple, si l'appelant nomme son tableau arr et que la fonction exécute declare -n arr="$1", la référence de nom arr pointe vers elle-même.

Convention à appliquer pour éviter ce problème : préfixez les variables internes de référence de nom par un trait de soulignement (par exemple, _arr, _ref, _out). Les appelants utilisent presque jamais de tels noms, ce qui rend les collisions extrêmement rares.

#!/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

Passer des tableaux associatifs (tables) par leur nom

Les tableaux associatifs (declare -A) fonctionnent de la même manière avec les références de nom. Passez le nom de la table sous forme de chaîne et déclarez une référence de nom dans la fonction : vous obtenez un accès complet en lecture et en écriture à toutes les paires clé-valeur.

C'est particulièrement utile pour les tables de configuration, les analyseurs d'options et les tables de recherche qui doivent être partagés entre plusieurs fonctions de bibliothèque.

#!/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

Fusionner deux tables avec une référence de nom

Comme les références de nom fournissent à la fonction un alias dynamique, vous pouvez implémenter proprement des utilitaires tels que la fusion de tables. Passez le nom de la table de destination et celui d'une ou plusieurs tables sources ; la fonction parcourt les clés sources et les écrit dans la destination.

Les références de nom permettent d'écrire une seule fonction générique merge_map qui fonctionne avec tous les tableaux associatifs de votre 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

Développement indirect : compatibilité avec les anciennes versions de Bash

Si vous devez prendre en charge Bash 4.0–4.2 (ou les anciens shells par défaut de macOS), declare -n n'est pas disponible. L'alternative est le développement indirect avec ${!varname}.

Le développement indirect évalue la valeur de varname comme un nom de variable et renvoie la valeur de cette variable. Pour les tableaux, vous le combinez avec une syntaxe spéciale afin d'obtenir tous les éléments.

Limites par rapport aux références de nom :

  • En pratique, il est en lecture seule (réécrire la valeur nécessite eval, qui comporte des risques d'injection)
  • Les clés des tableaux associatifs ne peuvent pas être parcourues de manière sûre sans eval
  • Utilisez les références de nom chaque fois que possible ; ne recourez au développement indirect qu'en cas de nécessité
#!/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"

Créer une bibliothèque de scripts modulaire

Dans les projets Bash professionnels, les fonctions utilitaires sont stockées dans des fichiers de bibliothèque (par exemple, lib/array_utils.sh) et chargées par les scripts qui en ont besoin.

Le modèle est le suivant :

  • Chaque fichier de bibliothèque définit uniquement des fonctions, sans effets de bord au niveau supérieur
  • Évitez les chargements multiples à l'aide d'une variable sentinelle : [[ -n $__LIB_LOADED ]] && return; __LIB_LOADED=1
  • Les scripts utilisateurs utilisent source ou . pour charger la bibliothèque
  • Les fonctions échangent les données complexes via des références de nom, ce qui conserve toute la logique de manière autonome

Vous obtenez ainsi des scripts faciles à tester, réutilisables et faciles à maintenir, plutôt que des astuces shell ponctuelles.

Exemple pratique : un analyseur d'options qui renvoie une table

Voici un cas d'utilisation concret : un analyseur d'options de ligne de commande qui remplit un tableau associatif fourni par l'appelant. Celui-ci passe le nom du tableau comme premier argument, suivi des arguments "$@" réels.

Ce modèle permet à n'importe quel script de réutiliser le même analyseur sans copier de code ni dépendre de variables globales.

#!/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]}"

Combiner des références de nom d'entrée et de sortie dans une même fonction

Les fonctions peuvent contenir plusieurs références de nom simultanément : une pour l'entrée et une pour la sortie. Cela permet de créer des fonctions de transformation pures : elles lisent une structure source, calculent un résultat et l'écrivent dans une structure de destination, le tout sans toucher à l'état global.

L'utilisation de noms distincts préfixés par un trait de soulignement pour chaque référence de nom rend les collisions impossibles, même lorsque les variables de l'appelant portent des noms 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

Vérification des connaissances : références de nom et passage de tableaux

Vérifiez votre compréhension du passage de tableaux et de tables entre les fonctions en Bash.

Récapitulatif : passer des structures de données complexes en Bash

Voici ce que vous avez appris dans cette leçon :

  • Les tableaux ne peuvent pas être passés par valeur : passez plutôt le nom de la variable sous forme de chaîne.
  • declare -n ref="$varname" crée une référence de nom : un alias dynamique vers la variable de l'appelant. Les lectures comme les écritures sont transmises à l'original. Nécessite Bash 4.3+.
  • Une collision de références de nom se produit lorsque les variables interne et externe portent le même nom. Évitez-la en préfixant les références de nom internes (par exemple, _arr, _map).
  • Les paramètres de sortie fonctionnent en passant le nom d'un tableau ou d'une table de résultat vide ; la fonction le remplit via une référence de nom.
  • La présence de plusieurs références de nom dans une même fonction permet des transformations pures de l'entrée vers la sortie, sans effets de bord globaux.
  • Le développement indirect (${!varname[@]} + eval) constitue une solution de repli plus ancienne pour Bash < 4.3, mais comporte des risques d'injection et est en pratique limité à la lecture.
  • Combinez ces techniques avec des fichiers de bibliothèque chargés pour créer des bases de code Bash modulaires, réutilisables et faciles à tester.
Gratuit pour commencer

Apprends Bash avec un tuteur IA — gratuit

Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.

Cours
22
Leçons
88

Questions Fréquemment Posées

La leçon « Transmettre des tableaux et des cartes associatives entre fonctions » est-elle gratuite ?

Oui — le texte complet de « Transmettre des tableaux et des cartes associatives entre fonctions » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Linux Command Line & Bash Scripting Mastery, passe à CoddyKit PRO. Le cours Linux Command Line & Bash Scripting Mastery comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Transmettre des tableaux et des cartes associatives entre fonctions » ?

Utilisez les références nommées et l’expansion indirecte pour transmettre en toute sécurité des structures de données complexes aux fonctions et en récupérer les résultats. Tu pratiques Linux Command Line & Bash Scripting Mastery avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Linux Command Line & Bash Scripting Mastery ?

Aucune expérience préalable n'est requise. Linux Command Line & Bash Scripting Mastery sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « Transmettre des tableaux et des cartes associatives entre fonctions » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Linux Command Line & Bash Scripting Mastery ?

Oui. Chaque leçon Linux Command Line & Bash Scripting Mastery inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Concevoir des fonctions avec une portée locale et des codes de retour
  2. Créer et charger des bibliothèques Bash réutilisables
  3. Analyser les options et les arguments avec getopts
  4. Transmettre des tableaux et des cartes associatives entre fonctions
← Retour à Linux Command Line & Bash Scripting Mastery