Taulukoiden ja assosiatiivisten karttojen välittäminen funktioiden välillä
Käyttäkää nameref-viittauksia ja epäsuoraa laajennusta monimutkaisten tietorakenteiden turvalliseen välittämiseen funktioihin ja niistä pois.
Taulukoiden ja assosiatiivisten karttojen välittäminen funktioiden välillä on ilmainen DevOps-bootcamp-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu DevOps-bootcamp-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. DevOps-bootcamp-kurssilla on yhteensä 4 oppituntia.
Miksi taulukoiden välittäminen funktioille on hankalaa
Bashissa taulukot ja assosiatiiviset taulukot eivät ole ensiluokkaisia arvoja. Kun yrität välittää taulukon funktiolle, et voi käyttää suoraan muotoa $my_array — se laajentaa vain ensimmäisen alkion.
Tarkastellaan tätä toimimatonta yritystä:
my_func "${my_array[@]}"Funktio saa litteän listan sanoja. Se ei voi tietää, missä yksi alkio päättyy ja seuraava alkaa, ja taulukon rakenne katoaa kokonaan.
Bash tarjoaa kaksi luotettavaa tapaa välittää monimutkaisia tietorakenteita:
- Nimiviitteet (
declare -n) — viite toiseen muuttujaan sen nimen perusteella (Bash 4.3+) - Epäsuora laajennus (
${!varname[@]}) — käsiksi pääseminen muuttujaan, jonka nimi on tallennettu toiseen muuttujaan (vanhemmat Bash-versiot)
Tässä oppitunnissa käsitellään molemmat tekniikat, jotta voitte kirjoittaa vankkoja ja uudelleenkäytettäviä funktioita, jotka toimivat taulukoiden ja karttojen kanssa.
Taulukon välittäminen nimen perusteella nimiviitteen avulla
Siistein nykyaikainen tapa on käyttää nimiviitettä: välität taulukon nimen merkkijonona ja määrittelet funktion sisällä paikallisen muuttujan, joka viittaa siihen.
Keskeinen syntaksi:
declare -n ref="$1"—refon nyt alias muuttujalle, jonka nimi välitettiin ensimmäisenä argumenttina- Luet, iteroit ja muokkaat
ref-muuttujaa aivan kuin se olisi alkuperäinen taulukko
Näin vältetään koko taulukon kopioiminen, ja funktio voi käsitellä minkä tahansa kokoisia taulukoita.
#!/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 fruitsKutsujan taulukon muokkaaminen funktion sisällä
Koska nimiviite on todellinen alias, nimiviitteen kautta tehdyt kirjoitukset muuttavat kutsujan alkuperäistä muuttujaa. Arvoja ei tarvitse tulostaa takaisin eikä yleisiä muuttujia tarvitse käyttää.
Tämän vuoksi nimiviitteet sopivat erinomaisesti funktioille, jotka muuntavat taulukoita paikallaan — esimerkiksi merkkijonojen normalisointiin, alkioiden suodattamiseen tai lajitteluun.
#!/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[@]}"Uuden taulukon palauttaminen nimiviitteen kautta
Funktio voi myös kirjoittaa kutsujan antamaan taulukkoon hyväksymällä sen nimen tulosparametrina. Kutsuja luo tyhjän taulukon, välittää sen nimen, ja funktio täyttää sen.
Tämä toimintamalli vastaa klassista C-kielen tapaa välittää osoitin tulospuskuriin — paitsi että tässä kaikki tehdään shellissä.
#!/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[@]}"Nimiviitteen nimiristiriidan sudenkuoppa
Nimiviitteisiin liittyy yksi kriittinen sudenkuoppa: jos paikallisella nimiviitemuuttujalla on sama nimi kuin kutsujan muuttujalla, Bash joutuu loputtomaan itseensä viittaavaan silmukkaan ja komentosarja epäonnistuu.
Jos kutsuja esimerkiksi nimeää taulukkonsa arr ja funktio suorittaa declare -n arr="$1", nimiviite arr viittaa itseensä.
Vältä tämä käyttämällä seuraavaa käytäntöä: lisää sisäisten nimiviitemuuttujien eteen alaviiva (esimerkiksi _arr, _ref, _out). Kutsujat käyttävät tällaisia nimiä erittäin harvoin, joten ristiriidat ovat hyvin epätodennäköisiä.
#!/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 arrAssosiatiivisten taulukoiden (karttojen) välittäminen nimen perusteella
Assosiatiiviset taulukot (declare -A) toimivat nimiviitteiden kanssa samalla tavalla. Välitä kartan nimi merkkijonona ja määrittele funktion sisällä nimiviite — saat täydet luku- ja kirjoitusoikeudet kaikkiin avain-arvo-pareihin.
Tämä on erittäin hyödyllistä määritys kartoissa, valitsinten jäsentimissä ja hakutaulukoissa, joita useiden kirjastofunktioiden täytyy käyttää yhdessä.
#!/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 configKahden kartan yhdistäminen nimiviitteen avulla
Koska nimiviitteet antavat funktiolle suoran aliaksen, voit toteuttaa esimerkiksi karttojen yhdistämisen siististi. Välitä kohdekartan ja yhden tai useamman lähdekartan nimet; funktio käy lähteiden avaimet läpi ja kirjoittaa ne kohteeseen.
Nimiviitteiden ansiosta voit kirjoittaa yhden yleiskäyttöisen merge_map-funktion, joka toimii kaikkien komentosarjasi assosiatiivisten taulukoiden kanssa.
#!/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]}"
doneEpäsuora laajennus: yhteensopivuus vanhempien Bash-versioiden kanssa
Jos sinun on tuettava Bash-versioita 4.0–4.2 (tai vanhempia macOS:n oletuskuoria), declare -n ei ole käytettävissä. Vaihtoehtona on epäsuora laajennus käyttäen muotoa ${!varname}.
Epäsuora laajennus tulkitsee varname-muuttujan arvon muuttujan nimeksi ja palauttaa kyseisen muuttujan arvon. Taulukoiden kanssa siihen yhdistetään erityinen syntaksi kaikkien alkioiden hakemista varten.
Rajoitukset nimiviitteisiin verrattuna:
- Käytännössä vain lukuun (takaisin kirjoittaminen edellyttää
eval-komentoa, johon liittyy injektioriskejä) - Assosiatiivisen taulukon avaimia ei voi iteroida turvallisesti ilman
eval-komentoa - Käyttäkää nimiviitteitä aina kun mahdollista ja turvautukaa epäsuoraan laajennukseen vain tarvittaessa
#!/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"Modulaarisen komentosarjakirjaston rakentaminen
Ammattimaisissa Bash-projekteissa apufunktiot tallennetaan kirjastotiedostoihin (esimerkiksi lib/array_utils.sh), ja niitä käyttävät komentosarjat lataavat tiedostot lähdekoodiksi.
Toimintamalli on seuraava:
- Jokainen kirjastotiedosto määrittelee vain funktioita — ylätason sivuvaikutuksia ei ole
- Estä tiedoston lataaminen lähdekoodiksi useita kertoja vartiomuuttujalla:
[[ -n $__LIB_LOADED ]] && return; __LIB_LOADED=1 - Käyttäjäkomentosarjat lataavat kirjaston komennolla
sourcetai. - Funktiot vaihtavat monimutkaisia tietoja nimiviitteiden avulla, jolloin kaikki logiikka pysyy itsenäisenä
Tuloksena on testattavia, uudelleenkäytettäviä ja ylläpidettäviä komentosarjoja — ei vain kertakäyttöisiä shell-virityksiä.
Käytännön esimerkki: kartan palauttava valitsin jäsentävä funktio
Käytännön käyttötapaus: komentorivin valitsin jäsentävä funktio, joka täyttää kutsujan antaman assosiatiivisen taulukon. Kutsuja välittää taulukon nimen ensimmäisenä argumenttina ja sen jälkeen varsinaiset "$@"-argumentit.
Tämän toimintamallin ansiosta mikä tahansa komentosarja voi käyttää samaa jäsentäjää uudelleen ilman koodin kopioimista tai yleisiin muuttujiin turvautumista.
#!/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]}"Syöte- ja tulosnimiviitteiden yhdistäminen samassa funktiossa
Funktiossa voi olla useita nimiviitteitä samanaikaisesti — yksi syötteelle ja yksi tulosteelle. Näin voidaan toteuttaa puhtaita muunnosfunktioita: ne lukevat lähderakenteesta, laskevat tuloksen ja kirjoittavat sen kohderakenteeseen koskematta lainkaan yleiseen tilaan.
Kullekin nimiviitteelle käytettävät erilliset alaviivalla alkavat nimet estävät ristiriidat, vaikka kutsujan muuttujilla olisi yksinkertaiset nimet.
#!/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]}"
doneTietojen tarkistus: nimiviitteet ja taulukoiden välittäminen
Testaa ymmärryksesi taulukoiden ja karttojen välittämisestä Bash-funktioiden välillä.
Kertaus: monimutkaisten tietorakenteiden välittäminen Bashissa
Tässä ovat oppitunnin tärkeimmät opit:
- Taulukoita ei voi välittää arvona — välitä sen sijaan muuttujan nimi merkkijonona.
declare -n ref="$varname"luo nimiviitteen: suoran aliaksen kutsujan muuttujaan. Sekä luku että kirjoitus kohdistuvat alkuperäiseen muuttujaan. Vaatii Bash-version 4.3 tai uudemman.- Nimiviitteen nimiristiriita syntyy, kun sisäisellä ja ulkoisella muuttujalla on sama nimi. Estä tämä lisäämällä sisäisten nimiviitteiden eteen esimerkiksi
_arrtai_map. - Tulosparametrit toimivat välittämällä tyhjän tulostaulukon tai -kartan nimen; funktio täyttää sen nimiviitteen kautta.
- Useat nimiviitteet samassa funktiossa mahdollistavat puhtaat syötteestä tulosteeksi -muunnokset ilman yleisiä sivuvaikutuksia.
- Epäsuora laajennus (
${!varname[@]}+eval) on vanhempi vaihtoehto Bash-versioille < 4.3, mutta siihen liittyy injektioriskejä, ja käytännössä se soveltuu vain lukemiseen. - Yhdistämällä nämä tekniikat lähdekoodiksi ladattaviin kirjastotiedostoihin voit rakentaa modulaarisia, uudelleenkäytettäviä ja testattavia Bash-koodikantoja.
Opi DevOps-bootcamp tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 142
- Oppitunnit
- 568
Usein kysytyt kysymykset
Onko oppitunti ”Taulukoiden ja assosiatiivisten karttojen välittäminen funktioiden välillä” ilmainen?
Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa DevOps-bootcamp-oppimispolun 3 oppituntia, myös oppitunnin “Taulukoiden ja assosiatiivisten karttojen välittäminen funktioiden välillä”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. DevOps-bootcamp-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Taulukoiden ja assosiatiivisten karttojen välittäminen funktioiden välillä”?
Käyttäkää nameref-viittauksia ja epäsuoraa laajennusta monimutkaisten tietorakenteiden turvalliseen välittämiseen funktioihin ja niistä pois. Harjoittelet DevOps-bootcamp-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni DevOps-bootcamp-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin DevOps-bootcamp-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.
Kuinka kauan ”Taulukoiden ja assosiatiivisten karttojen välittäminen funktioiden välillä”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä DevOps-bootcamp-oppitunnilla?
Kyllä. Jokainen DevOps-bootcamp-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- Funktioiden suunnittelu paikallisella näkyvyysalueella ja paluuarvokoodeilla
- Uudelleenkäytettävien Bash-kirjastojen rakentaminen ja lataaminen
- Lippujen ja argumenttien jäsentäminen getoptsilla
- Taulukoiden ja assosiatiivisten karttojen välittäminen funktioiden välillä