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 DevOps Bootcamp-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 DevOps Bootcamp-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der DevOps Bootcamp-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"–refist 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 fruitsDas 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 arrAssoziative 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 configZwei 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]}"
doneIndirekte 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
evalnicht 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
sourceoder. - 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]}"
doneWissenscheck: 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 DevOps Bootcamp-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der DevOps Bootcamp-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 DevOps Bootcamp 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 DevOps Bootcamp zu starten?
Keine Vorkenntnisse erforderlich. DevOps Bootcamp 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 DevOps Bootcamp-Lektion Code schreiben und ausführen?
Ja. Jede DevOps Bootcamp-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
- Funktionen mit lokalem Gültigkeitsbereich und Rückgabecodes entwerfen
- Wiederverwendbare Bash-Bibliotheken erstellen und sourcen
- Flags und Argumente mit getopts parsen
- Arrays und assoziative Maps zwischen Funktionen übergeben