Overførsel af arrays og associative maps mellem funktioner
Brug namerefs og indirekte expansion til sikkert at overføre komplekse datastrukturer til og fra funktioner.
Overførsel af arrays og associative maps mellem funktioner er en gratis Intensiv DevOps-uddannelse-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Intensiv DevOps-uddannelse, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Intensiv DevOps-uddannelse-kurset indeholder 4 lektioner i alt.
Hvorfor det er tricky at overføre arrays til funktioner
I Bash er arrays og associative arrays ikke værdier af første rang. Når du forsøger at overføre et array til en funktion, kan du ikke bare bruge $my_array — det udvider kun det første element.
Overvej dette fejlslagne forsøg:
my_func "${my_array[@]}"Funktionen modtager en flad liste af ord. Den har ingen mulighed for at vide, hvor ét element slutter, og det næste begynder, og array-strukturen går helt tabt.
Bash tilbyder to pålidelige mekanismer til at overføre komplekse datastrukturer:
- Namerefs (
declare -n) — en reference til en anden variabel via dens navn (Bash 4.3+) - Indirekte udvidelse (
${!varname[@]}) — adgang til en variabel, hvis navn er gemt i en anden variabel (ældre Bash)
Denne lektion gennemgår begge teknikker, så du kan skrive robuste, genanvendelige funktioner, der arbejder med arrays og maps.
Overførsel af et array via navn med en nameref
Den reneste moderne tilgang er en nameref: du overfører arrayets navn som en streng, og inde i funktionen erklærer du en lokal variabel, der refererer til det.
Den vigtigste syntaks:
declare -n ref="$1"—refer nu et alias for den variabel, hvis navn blev overført som det første argument- Du læser, gennemløber og ændrer
refpræcis som det oprindelige array
Det undgår at kopiere hele arrayet og lader funktionen arbejde med arrays af enhver størrelse.
#!/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Ændring af den kaldende funktions array inde i en funktion
Fordi en nameref er et ægte alias, ændrer skrivninger gennem namerefen den kaldende funktions oprindelige variabel. Der er ikke behov for at skrive værdier tilbage med echo eller bruge globale variabler.
Det gør namerefs ideelle til funktioner, der transformerer arrays på stedet — for eksempel ved at normalisere strenge, filtrere elementer eller sortere.
#!/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[@]}"Returnering af et nyt array gennem en nameref
En funktion kan også skrive til et array, som den kaldende funktion har leveret, ved at acceptere dets navn som en outputparameter. Den kaldende funktion opretter et tomt array, overfører dets navn, og funktionen udfylder det.
Dette mønster minder om den klassiske C-idiom med at overføre en pointer til en resultatbuffer — bortset fra at det her udføres helt i 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[@]}"Fælden med nameref-kollisioner
Der er én kritisk faldgrube ved namerefs: Hvis den lokale nameref-variabel har samme navn som den kaldende funktions variabel, går Bash ind i en uendelig selvreferenceløkke, og scriptet mislykkes.
Hvis den kaldende funktion for eksempel kalder sit array arr, og funktionen udfører declare -n arr="$1", peger namerefen arr på sig selv.
Konvention, der undgår dette: Sæt et underscore foran interne nameref-variabler (f.eks. _arr, _ref, _out). Kaldende funktioner bruger næsten aldrig sådanne navne, så kollisioner bliver ekstremt sjældne.
#!/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 arrOverførsel af associative arrays (maps) via navn
Associative arrays (declare -A) fungerer på samme måde med namerefs. Overfør mappets navn som en streng, og erklær en nameref inde i funktionen — så får du fuld læse- og skriveadgang til alle nøgleværdi-par.
Det er uvurderligt til konfigurationsmaps, optionfortolkere og opslagstabeller, der skal deles mellem biblioteksfunktioner.
#!/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 configSammenfletning af to maps med en nameref
Fordi namerefs giver funktionen et aktivt alias, kan du implementere hjælpefunktioner som sammenfletning af maps på en enkel måde. Overfør destinationsmappet og et eller flere kildemaps via navn; funktionen gennemløber kildekeys og skriver dem ind i destinationen.
Namerefs gør det muligt at skrive én generisk merge_map-funktion, der fungerer med alle associative arrays i dit 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]}"
doneIndirekte udvidelse: kompatibilitet med ældre Bash
Hvis du skal understøtte Bash 4.0–4.2 (eller ældre standardskaller på macOS), er declare -n ikke tilgængelig. Alternativet er indirekte udvidelse med ${!varname}.
Indirekte udvidelse fortolker værdien af varname som et variabelnavn og returnerer variablens værdi. For arrays kombinerer du den med en særlig syntaks for at få alle elementer.
Begrænsninger sammenlignet med namerefs:
- I praksis skrivebeskyttet (tilbageskrivning kræver
eval, som medfører risiko for injektion) - Associative array-nøgler kan ikke gennemløbes sikkert uden
eval - Brug namerefs, hvor det er muligt; brug kun indirekte udvidelse som alternativ, når det er nødvendigt
#!/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"Opbygning af et modulært scriptbibliotek
I professionelle Bash-projekter gemmes hjælpefunktioner i biblioteksfiler (f.eks. lib/array_utils.sh) og indlæses af de scripts, der har brug for dem.
Mønstret er:
- Hver biblioteksfil definerer kun funktioner — ingen bivirkninger på øverste niveau
- Undgå at indlæse filen flere gange med en markørvariabel:
[[ -n $__LIB_LOADED ]] && return; __LIB_LOADED=1 - Forbrugerscripts bruger
sourceeller.til at indlæse biblioteket - Funktioner udveksler komplekse data via namerefs, så al logik forbliver selvstændig
Det giver scripts, der kan testes, genbruges og vedligeholdes — ikke engangshacks i shell.
Praktisk eksempel: optionfortolker, der returnerer et map
Et eksempel fra virkeligheden: en fortolker af kommandolinjeoptioner, der udfylder et associativt array, som den kaldende funktion har leveret. Den kaldende funktion overfører arrayets navn som det første argument efterfulgt af de faktiske "$@"-argumenter.
Dette mønster lader ethvert script genbruge den samme fortolker uden at kopiere kode eller være afhængigt af globale variabler.
#!/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]}"Kombination af input- og outputnamerefs i én funktion
Funktioner kan have flere namerefs samtidig — én til input og én til output. Det muliggør rene transformationsfunktioner: De læser fra en kildestruktur, beregner et resultat og skriver det til en destinationsstruktur, alt sammen uden at ændre global tilstand.
Ved at bruge forskellige navne med underscore foran til hver nameref undgår du kollisioner, selv når de kaldende funktioners variabler har enkle navne.
#!/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]}"
doneTest din viden: namerefs og overførsel af arrays
Test din forståelse af, hvordan arrays og maps overføres mellem funktioner i Bash.
Opsummering: Overførsel af komplekse datastrukturer i Bash
Dette har du lært i lektionen:
- Arrays kan ikke overføres som værdier — overfør i stedet variablens navn som en streng.
declare -n ref="$varname"opretter en nameref: et aktivt alias til den kaldende funktions variabel. Både læsninger og skrivninger går videre til den oprindelige variabel. Kræver Bash 4.3+.- Nameref-kollision opstår, når den interne og den eksterne variabel har samme navn. Forebyg det ved at sætte et underscore foran interne namerefs (f.eks.
_arr,_map). - Outputparametre fungerer ved at overføre navnet på et tomt resultat-array eller -map; funktionen udfylder det gennem en nameref.
- Flere namerefs i én funktion muliggør rene transformationer fra input til output uden globale bivirkninger.
- Indirekte udvidelse (
${!varname[@]}+eval) er et ældre alternativ til Bash < 4.3, men medfører risiko for injektion og er i praksis skrivebeskyttet. - Kombiner disse teknikker med indlæste biblioteksfiler for at opbygge modulære, genanvendelige og testbare Bash-kodebaser.
Lær Intensiv DevOps-uddannelse med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 142
- Lektioner
- 568
Ofte stillede spørgsmål
Er lektionen “Overførsel af arrays og associative maps mellem funktioner” gratis?
Ja — alle 3 lektioner i læringssporet Intensiv DevOps-uddannelse, inklusive “Overførsel af arrays og associative maps mellem funktioner”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Intensiv DevOps-uddannelse-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Overførsel af arrays og associative maps mellem funktioner”?
Brug namerefs og indirekte expansion til sikkert at overføre komplekse datastrukturer til og fra funktioner. Du øver dig i Intensiv DevOps-uddannelse med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på Intensiv DevOps-uddannelse?
Der kræves ingen tidligere erfaring. Intensiv DevOps-uddannelse på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.
Hvor lang tid tager lektionen “Overførsel af arrays og associative maps mellem funktioner”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne Intensiv DevOps-uddannelse-lektion?
Ja. Alle Intensiv DevOps-uddannelse-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Design af funktioner med lokalt scope og return codes
- Opbygning og sourcing af genanvendelige Bash-biblioteker
- Parsing af flags og argumenter med getopts
- Overførsel af arrays og associative maps mellem funktioner