0Pricing
Linux Command Line & Bash Scripting Mastery · Lektion

Arrays und assoziative Maps zwischen Funktionen übergeben

Verwenden Sie Namerefs und indirekte Expansion, um komplexe Datenstrukturen sicher an Funktionen zu übergeben und aus ihnen zurückzugeben.

Arrays und assoziative Maps zwischen Funktionen übergeben ist eine kostenlose Linux Command Line & Bash Scripting Mastery-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Linux Command Line & Bash Scripting Mastery-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Linux Command Line & Bash Scripting Mastery-Kurs umfasst insgesamt 4 Lektionen.

Warum das Übergeben von Arrays an Funktionen knifflig ist

In Bash sind Arrays und assoziative Arrays keine Werte erster Klasse. Wenn Sie versuchen, ein Array an eine Funktion zu übergeben, können Sie nicht einfach $my_array verwenden – dadurch wird nur das erste Element expandiert.

Betrachten Sie diesen fehlerhaften Versuch:

my_func "${my_array[@]}"

Die Funktion erhält eine flache Liste von Wörtern. Sie kann nicht erkennen, wo ein Element endet und das nächste beginnt, und die Array-Struktur geht vollständig verloren.

Bash stellt zwei zuverlässige Mechanismen zum Übergeben komplexer Datenstrukturen bereit:

  • Namerefs (declare -n) – eine Referenz auf eine andere Variable anhand ihres Namens (Bash 4.3+)
  • Indirekte Expansion (${!varname[@]}) – Zugriff auf eine Variable, deren Name in einer anderen Variable gespeichert ist (ältere Bash-Versionen)

In dieser Lektion behandeln Sie beide Techniken, damit Sie robuste, wiederverwendbare Funktionen schreiben können, die mit Arrays und Maps arbeiten.

Ein Array anhand seines Namens mit einem Nameref übergeben

Der sauberste moderne Ansatz ist ein Nameref: Sie übergeben den Namen des Arrays als Zeichenkette und deklarieren innerhalb der Funktion eine lokale Variable, die darauf verweist.

Die wichtige Syntax:

  • declare -n ref="$1" – ref ist nun ein Alias für den Variablennamen, der als erstes Argument übergeben wurde
  • Sie lesen ref, iterieren darüber und ändern es genau so, als wäre es das ursprüngliche Array

Dadurch wird vermieden, das gesamte Array zu kopieren, und die Funktion kann mit Arrays beliebiger Größe arbeiten.

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

Das Array des Aufrufers innerhalb einer Funktion ändern

Da ein Nameref ein echter Alias ist, ändern Schreibzugriffe über das Nameref die ursprüngliche Variable des Aufrufers. Sie müssen weder Werte mit echo zurückgeben noch globale Variablen verwenden.

Dadurch eignen sich Namerefs ideal für Funktionen, die Arrays direkt ändern – beispielsweise zum Normalisieren von Zeichenketten, Filtern von Elementen oder Sortieren.

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

Ein neues Array über ein Nameref zurückgeben

Eine Funktion kann auch in ein vom Aufrufer bereitgestelltes Array schreiben, indem sie dessen Namen als Ausgabeparameter akzeptiert. Der Aufrufer erstellt ein leeres Array, übergibt dessen Namen, und die Funktion füllt es.

Dieses Muster entspricht dem klassischen C-Idiom, einen Zeiger auf einen Ergebnis-Puffer zu übergeben – nur wird es hier vollständig in der Shell umgesetzt.

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

Die Nameref-Falle durch Namenskollisionen

Bei Namerefs gibt es eine wichtige Falle: Wenn die lokale Nameref-Variable denselben Namen wie die Variable des Aufrufers hat, gerät Bash in eine Endlosschleife durch Selbstreferenz und das Skript schlägt fehl.

Wenn der Aufrufer sein Array beispielsweise arr nennt und die Funktion declare -n arr="$1" ausführt, verweist das Nameref arr auf sich selbst.

Konvention zur Vermeidung dieses Problems: Stellen Sie internen Nameref-Variablen einen Unterstrich voran (z. B. _arr, _ref, _out). Aufrufer verwenden solche Namen so gut wie nie, wodurch Kollisionen äußerst selten werden.

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

Assoziative Arrays (Maps) anhand ihres Namens übergeben

Assoziative Arrays (declare -A) funktionieren mit Namerefs genauso. Übergeben Sie den Namen der Map als Zeichenkette und deklarieren Sie innerhalb der Funktion ein Nameref – dadurch erhalten Sie vollständigen Lese- und Schreibzugriff auf alle Schlüssel-Wert-Paare.

Das ist besonders nützlich für Konfigurations-Maps, Optionsparser und Nachschlagetabellen, die von mehreren Bibliotheksfunktionen gemeinsam verwendet werden müssen.

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

Zwei Maps mit einem Nameref zusammenführen

Da Namerefs der Funktion einen direkt verknüpften Alias geben, können Sie Dienstprogramme wie das Zusammenführen von Maps auf saubere Weise implementieren. Übergeben Sie die Ziel-Map und eine oder mehrere Quell-Maps anhand ihrer Namen. Die Funktion durchläuft die Schlüssel der Quellen und schreibt sie in das Ziel.

Namerefs ermöglichen eine einzige generische Funktion merge_map, die mit beliebigen assoziativen Arrays in Ihrem Skript funktioniert.

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

Indirekte Expansion: Kompatibilität mit älteren Bash-Versionen

Wenn Sie Bash 4.0–4.2 (oder die älteren Standardshells von macOS) unterstützen müssen, ist declare -n nicht verfügbar. Die Alternative ist die indirekte Expansion mit ${!varname}.

Bei der indirekten Expansion wird der Wert von varname als Variablenname ausgewertet und der Wert dieser Variable zurückgegeben. Für Arrays kombinieren Sie sie mit einer speziellen Syntax, um alle Elemente zu erhalten.

Einschränkungen gegenüber Namerefs:

  • In der Praxis schreibgeschützt (das Zurückschreiben erfordert eval, was Risiken durch Code-Injection birgt)
  • Schlüssel assoziativer Arrays können ohne eval nicht sicher durchlaufen werden
  • Verwenden Sie nach Möglichkeit Namerefs und greifen Sie nur bei Bedarf auf die indirekte Expansion zurück
#!/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"

Eine modulare Skriptbibliothek erstellen

In professionellen Bash-Projekten werden Hilfsfunktionen in Bibliotheksdateien (z. B. lib/array_utils.sh) gespeichert und von Skripten eingebunden, die sie benötigen.

Das Muster sieht folgendermaßen aus:

  • Jede Bibliotheksdatei definiert ausschließlich Funktionen – keine Seiteneffekte auf oberster Ebene
  • Verhindern Sie das doppelte Einbinden mit einer Sentinel-Variable: [[ -n $__LIB_LOADED ]] && return; __LIB_LOADED=1
  • Verbraucherskripte laden die Bibliothek mit source oder .
  • Funktionen tauschen komplexe Daten über Namerefs aus, sodass die gesamte Logik in sich geschlossen bleibt

So entstehen testbare, wiederverwendbare und wartbare Skripte statt einmaliger Shell-Hacks.

Praxisbeispiel: Optionsparser, der eine Map zurückgibt

Ein praktischer Anwendungsfall: ein Kommandozeilen-Optionsparser, der ein vom Aufrufer bereitgestelltes assoziatives Array füllt. Der Aufrufer übergibt den Array-Namen als erstes Argument, gefolgt von den tatsächlichen "$@"-Argumenten.

Mit diesem Muster kann jedes Skript denselben Parser wiederverwenden, ohne Code zu kopieren oder auf globale Variablen angewiesen zu sein.

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

Eingabe- und Ausgabe-Namerefs in einer Funktion kombinieren

Funktionen können mehrere Namerefs gleichzeitig enthalten – eines für die Eingabe und eines für die Ausgabe. Dadurch lassen sich reine Transformationsfunktionen erstellen: Sie lesen aus einer Quellstruktur, berechnen ein Ergebnis und schreiben es in eine Zielstruktur, ohne den globalen Zustand zu verändern.

Durch unterschiedliche, mit einem Unterstrich beginnende Namen für jedes Nameref werden Kollisionen vermieden, selbst wenn die Variablen des Aufrufers einfache Namen haben.

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

Wissenscheck: Namerefs und das Übergeben von Arrays

Testen Sie Ihr Verständnis vom Übergeben von Arrays und Maps zwischen Funktionen in Bash.

Zusammenfassung: Komplexe Datenstrukturen in Bash übergeben

Das haben Sie in dieser Lektion gelernt:

  • Arrays können nicht als Wert übergeben werden – übergeben Sie stattdessen den Variablennamen als Zeichenkette.
  • declare -n ref="$varname" erstellt ein Nameref: einen direkt verknüpften Alias für die Variable des Aufrufers. Sowohl Lese- als auch Schreibzugriffe werden an die ursprüngliche Variable weitergeleitet. Erfordert Bash 4.3+.
  • Eine Nameref-Kollision tritt auf, wenn die interne und die externe Variable denselben Namen haben. Verhindern Sie dies, indem Sie internen Namerefs einen Präfix voranstellen (z. B. _arr, _map).
  • Ausgabeparameter funktionieren, indem der Name eines leeren Ergebnis-Arrays oder einer leeren Ergebnis-Map übergeben wird. Die Funktion füllt sie über ein Nameref.
  • Mehrere Namerefs in einer Funktion ermöglichen reine Transformationen von der Eingabe zur Ausgabe ohne globale Seiteneffekte.
  • Die indirekte Expansion (${!varname[@]} + eval) ist eine ältere Ausweichlösung für Bash < 4.3, birgt jedoch Risiken durch Code-Injection und ist in der Praxis schreibgeschützt.
  • Kombinieren Sie diese Techniken mit eingebundenen Bibliotheksdateien, um modulare, wiederverwendbare und testbare Bash-Codebasen zu erstellen.

Häufig gestellte Fragen

Ist die Lektion „Arrays und assoziative Maps zwischen Funktionen übergeben“ kostenlos?

Ja — der vollständige Text von „Arrays und assoziative Maps zwischen Funktionen übergeben“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Linux Command Line & Bash Scripting Mastery-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Linux Command Line & Bash Scripting Mastery-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Arrays und assoziative Maps zwischen Funktionen übergeben“?

Verwenden Sie Namerefs und indirekte Expansion, um komplexe Datenstrukturen sicher an Funktionen zu übergeben und aus ihnen zurückzugeben. Du übst Linux Command Line & Bash Scripting Mastery mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Linux Command Line & Bash Scripting Mastery zu starten?

Keine Vorkenntnisse erforderlich. Linux Command Line & Bash Scripting Mastery auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.

Wie lange dauert die Lektion „Arrays und assoziative Maps zwischen Funktionen übergeben“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Linux Command Line & Bash Scripting Mastery-Lektion Code schreiben und ausführen?

Ja. Jede Linux Command Line & Bash Scripting Mastery-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Funktionen mit lokalem Gültigkeitsbereich und Rückgabecodes entwerfen
  2. Wiederverwendbare Bash-Bibliotheken erstellen und sourcen
  3. Flags und Argumente mit getopts parsen
  4. Arrays und assoziative Maps zwischen Funktionen übergeben
← Zurück zu Linux Command Line & Bash Scripting Mastery