Sende arrayer og assosiative map mellom funksjoner
Bruk namerefs og indirekte ekspansjon til å sende komplekse datastrukturer trygt inn i og ut av funksjoner.
Sende arrayer og assosiative map mellom funksjoner er en gratis leksjon i DevOps-bootcamp på CoddyKit. Dette er leksjon 4 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i DevOps-bootcamp, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i DevOps-bootcamp inneholder totalt 4 leksjoner.
Hvorfor det er vanskelig å sende tabeller til funksjoner
I Bash er tabeller og assosiative tabeller ikke førsteklasses verdier. Når du prøver å sende en tabell til en funksjon, kan du ikke bare bruke $my_array — da utvides bare det første elementet.
Se på dette mislykkede forsøket:
my_func "${my_array[@]}"Funksjonen mottar en flat liste med ord. Den har ingen mulighet til å vite hvor ett element slutter og det neste begynner, og tabellstrukturen går fullstendig tapt.
Bash tilbyr to pålitelige mekanismer for å sende komplekse datastrukturer:
- Namereferanser (
declare -n) — en referanse til en annen variabel via navnet (Bash 4.3+) - Indirekte utvidelse (
${!varname[@]}) — tilgang til en variabel som har et navn lagret i en annen variabel (eldre Bash-versjoner)
I denne leksjonen gjennomgår De begge teknikkene, slik at De kan skrive robuste, gjenbrukbare funksjoner som fungerer med tabeller og map-er.
Sende en tabell via navn ved hjelp av en namereferanse
Den ryddigste moderne fremgangsmåten er en namereferanse: De sender navnet på tabellen som en streng, og inne i funksjonen deklarerer De en lokal variabel som refererer til den.
Viktig syntaks:
declare -n ref="$1"—refer nå et alias for variabelnavnet som ble sendt som det første argumentet- De leser, itererer over og endrer
refnøyaktig som om det var den opprinnelige tabellen
Dette unngår å kopiere hele tabellen og lar funksjonen arbeide med tabeller av 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 fruitsEndre den kallende partens tabell inne i en funksjon
Fordi en namereferanse er et ekte alias, endres den opprinnelige variabelen hos den kallende parten når De skriver via namereferansen. Det er ikke nødvendig å skrive ut verdier for å sende dem tilbake eller å bruke globale variabler.
Dette gjør namereferanser ideelle for funksjoner som transformerer tabeller direkte — for eksempel ved å normalisere strenger, 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[@]}"Returnere en ny tabell via en namereferanse
En funksjon kan også skrive til en tabell som den kallende parten angir, ved å ta imot navnet som en utdataparameter. Den kallende parten oppretter en tom tabell, sender navnet, og funksjonen fyller den ut.
Dette mønsteret tilsvarer det klassiske C-idiomet der De sender en peker til en resultatbuffer — bortsett fra at det her gjøres utelukkende i skallet.
#!/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[@]}"Fallgruven med namereferanse-kollisjoner
Det finnes én kritisk fallgruve med namereferanser: Hvis den lokale namereferansevariabelen har samme navn som variabelen hos den kallende parten, går Bash inn i en uendelig selvreferanseløkke, og skriptet mislykkes.
Hvis den kallende parten for eksempel kaller tabellen sin arr, og funksjonen gjør declare -n arr="$1", peker namereferansen arr på seg selv.
Konvensjon for å unngå dette: Sett en understrek som prefiks på interne namereferansevariabler (for eksempel _arr, _ref, _out). Kallende parter bruker nesten aldri slike navn, så kollisjoner blir svært sjeldne.
#!/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 arrSende assosiative tabeller (map-er) via navn
Assosiative tabeller (declare -A) fungerer på samme måte med namereferanser. Send navnet på map-et som en streng, og deklarer en namereferanse inne i funksjonen — da får De full lese- og skrivetilgang til alle nøkkel-verdi-parene.
Dette er svært nyttig for konfigurasjonsmap-er, alternativtolkere og oppslagstabeller som må deles mellom bibliotekfunksjoner.
#!/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 configSlå sammen to map-er med en namereferanse
Fordi namereferanser gir funksjonen et aktivt alias, kan De implementere verktøy som sammenslåing av map-er på en ryddig måte. Send målkartet og ett eller flere kildekart via navn; funksjonen itererer over kildenøkler og skriver dem til målet.
Namereferanser gjør det mulig å skrive én generell merge_map-funksjon som fungerer med alle assosiative tabeller i skriptet.
#!/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 utvidelse: kompatibilitet med eldre Bash-versjoner
Hvis De må støtte Bash 4.0–4.2 (eller eldre standardskall i macOS), er declare -n ikke tilgjengelig. Alternativet er indirekte utvidelse med ${!varname}.
Indirekte utvidelse tolker verdien av varname som et variabelnavn og returnerer verdien til denne variabelen. For tabeller kombinerer De dette med en spesiell syntaks for å hente alle elementene.
Begrensninger sammenlignet med namereferanser:
- I praksis skrivebeskyttet (tilbakeskriving krever
eval, som medfører risiko for injisering) - Nøkler i assosiative tabeller kan ikke itereres over på en sikker måte uten
eval - Bruk namereferanser når det er mulig; bruk indirekte utvidelse som reserveløsning bare når det er nødvendig
#!/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"Bygge et modulært skriptbibliotek
I profesjonelle Bash-prosjekter lagres hjelpefunksjoner i bibliotekfiler (for eksempel lib/array_utils.sh) og hentes inn av skript som trenger dem.
Mønsteret er:
- Hver bibliotekfil definerer bare funksjoner — ingen bivirkninger på toppnivå
- Unngå at filen hentes inn flere ganger ved hjelp av en vaktvariabel:
[[ -n $__LIB_LOADED ]] && return; __LIB_LOADED=1 - Skript som bruker biblioteket, benytter
sourceeller.for å laste det inn - Funksjoner utveksler komplekse data via namereferanser, slik at all logikk holdes samlet
Dette gir skript som kan testes, gjenbrukes og vedlikeholdes — ikke engangshack i skallet.
Praktisk eksempel: alternativtolker som returnerer et map
Et praktisk bruksområde er en kommandolinjetolker for alternativer som fyller ut en assosiativ tabell angitt av den kallende parten. Den kallende parten sender tabellnavnet som det første argumentet, etterfulgt av de faktiske "$@"-argumentene.
Dette mønsteret lar ethvert skript gjenbruke den samme tolken uten å kopiere kode eller være avhengig av 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]}"Kombinere inn- og utdata-namereferanser i én funksjon
Funksjoner kan ha flere namereferanser samtidig — én for inndata og én for utdata. Dette muliggjør rene transformasjonsfunksjoner: De leser fra en kildestruktur, beregner et resultat og skriver det til en målstruktur, uten å endre global tilstand.
Ved å bruke ulike navn med understrek som prefiks for hver namereferanse unngår De kollisjoner, selv når variablene hos den kallende parten har enkle navn.
#!/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]}"
doneKunnskapssjekk: namereferanser og tabeller som argumenter
Test forståelsen Deres av hvordan tabeller og map-er sendes mellom funksjoner i Bash.
Oppsummering: sende komplekse datastrukturer i Bash
Dette har De lært i denne leksjonen:
- Tabeller kan ikke sendes som verdier — send variabelens navn som en streng i stedet.
declare -n ref="$varname"oppretter en namereferanse: et aktivt alias til variabelen hos den kallende parten. Både lesing og skriving går videre til originalen. Krever Bash 4.3+.- Namereferanse-kollisjon oppstår når den interne og den eksterne variabelen har samme navn. Unngå dette ved å sette en understrek som prefiks på interne namereferanser (for eksempel
_arr,_map). - Utdataparametere fungerer ved at De sender navnet på en tom resultattabell eller et tomt resultatmap; funksjonen fyller det ut via en namereferanse.
- Flere namereferanser i én funksjon muliggjør rene transformasjoner fra inndata til utdata uten globale bivirkninger.
- Indirekte utvidelse (
${!varname[@]}+eval) er en eldre reserveløsning for Bash < 4.3, men medfører risiko for injisering og er i praksis skrivebeskyttet. - Kombiner disse teknikkene med bibliotekfiler som hentes inn for å bygge modulære, gjenbrukbare og testbare Bash-kodebaser.
Lær deg DevOps-bootcamp med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 142
- Leksjoner
- 568
Ofte stilte spørsmål
Er leksjonen «Sende arrayer og assosiative map mellom funksjoner» gratis?
Ja – du kan lese valgfritt 3 av leksjonene i læringsstien DevOps-bootcamp, inkludert «Sende arrayer og assosiative map mellom funksjoner», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i DevOps-bootcamp inneholder totalt 4 leksjoner.
Hva lærer jeg i «Sende arrayer og assosiative map mellom funksjoner»?
Bruk namerefs og indirekte ekspansjon til å sende komplekse datastrukturer trygt inn i og ut av funksjoner. Du øver på DevOps-bootcamp med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med DevOps-bootcamp?
Ingen tidligere erfaring er nødvendig. DevOps-bootcamp på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 4 av 4.
Hvor lang tid tar leksjonen «Sende arrayer og assosiative map mellom funksjoner»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne DevOps-bootcamp-leksjonen?
Ja. Alle DevOps-bootcamp-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Utforme funksjoner med lokalt scope og returkoder
- Bygge og hente inn gjenbrukbare Bash-biblioteker
- Tolke flagg og argumenter med getopts
- Sende arrayer og assosiative map mellom funksjoner