0Pricing
Linux Command Line & Bash Scripting Mastery · Pelajaran

Meneruskan Larik dan Peta Asosiatif antar Fungsi

Gunakan nameref dan ekspansi tidak langsung untuk meneruskan struktur data kompleks ke dalam dan ke luar fungsi dengan aman.

Meneruskan Larik dan Peta Asosiatif antar Fungsi adalah pelajaran Linux Command Line & Bash Scripting Mastery gratis di CoddyKit. Ini adalah pelajaran 4 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Linux Command Line & Bash Scripting Mastery, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Linux Command Line & Bash Scripting Mastery mencakup 4 pelajaran total.

Mengapa Meneruskan Larik ke Fungsi Itu Rumit

Dalam Bash, larik dan larik asosiatif bukan nilai kelas satu. Saat Anda mencoba meneruskan larik ke fungsi, Anda tidak dapat begitu saja menggunakan $my_array — itu hanya memperluas elemen pertama.

Perhatikan percobaan yang keliru berikut:

my_func "${my_array[@]}"

Fungsi menerima daftar kata datar. Fungsi tersebut tidak dapat mengetahui di mana satu elemen berakhir dan elemen berikutnya dimulai, serta kehilangan seluruh struktur larik.

Bash menyediakan dua mekanisme yang andal untuk meneruskan struktur data kompleks:

  • Referensi nama (declare -n) — referensi ke variabel lain berdasarkan namanya (Bash 4.3+)
  • Perluasan tidak langsung (${!varname[@]}) — mengakses variabel yang namanya disimpan dalam variabel lain (Bash versi lama)

Pelajaran ini membahas kedua teknik tersebut agar Anda dapat menulis fungsi yang tangguh dan dapat digunakan kembali untuk bekerja dengan larik dan peta.

Meneruskan Larik Berdasarkan Nama Menggunakan Referensi Nama

Pendekatan modern yang paling bersih adalah referensi nama: Anda meneruskan nama larik sebagai string, lalu di dalam fungsi mendeklarasikan variabel lokal yang merujuk ke larik tersebut.

Sintaks utama:

  • declare -n ref="$1" — kini ref adalah alias untuk nama variabel apa pun yang diteruskan sebagai argumen pertama
  • Anda membaca, melakukan iterasi, dan mengubah ref persis seperti larik aslinya

Dengan cara ini, seluruh larik tidak perlu disalin dan fungsi dapat bekerja dengan larik berukuran apa pun.

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

Mengubah Larik Pemanggil dari Dalam Fungsi

Karena referensi nama adalah alias yang sebenarnya, penulisan melalui referensi nama mengubah variabel asli milik pemanggil. Anda tidak perlu mengembalikan nilai dengan echo atau menggunakan variabel global.

Hal ini membuat referensi nama ideal untuk fungsi yang mengubah larik secara langsung — misalnya menormalkan string, menyaring elemen, atau mengurutkan.

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

Mengembalikan Larik Baru Melalui Referensi Nama

Fungsi juga dapat menulis ke larik yang disediakan pemanggil dengan menerima namanya sebagai parameter keluaran. Pemanggil membuat larik kosong, meneruskan namanya, lalu fungsi mengisinya.

Pola ini menyerupai idiom klasik C yang meneruskan penunjuk ke penyangga hasil — hanya saja di sini semuanya dilakukan di dalam 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[@]}"

Jebakan Benturan Referensi Nama

Ada satu masalah penting pada referensi nama: jika variabel referensi nama lokal memiliki nama yang sama dengan variabel milik pemanggil, Bash memasuki putaran referensi diri tanpa akhir dan skrip gagal.

Misalnya, jika pemanggil menamai lariknya arr dan fungsi menjalankan declare -n arr="$1", referensi nama arr menunjuk ke dirinya sendiri.

Konvensi untuk menghindarinya: awali variabel referensi nama internal dengan garis bawah (misalnya, _arr, _ref, _out). Pemanggil hampir tidak pernah menggunakan nama seperti itu, sehingga benturan sangat jarang terjadi.

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

Meneruskan Larik Asosiatif (Peta) Berdasarkan Nama

Larik asosiatif (declare -A) bekerja dengan cara yang sama menggunakan referensi nama. Teruskan nama peta sebagai string dan deklarasikan referensi nama di dalam fungsi — Anda mendapatkan akses baca/tulis penuh ke semua pasangan kunci-nilai.

Hal ini sangat berguna untuk peta konfigurasi, pengurai opsi, dan tabel pencarian yang harus digunakan bersama oleh berbagai fungsi pustaka.

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

Menggabungkan Dua Peta dengan Referensi Nama

Karena referensi nama memberi fungsi alias yang selalu terhubung, Anda dapat menerapkan utilitas seperti penggabungan peta dengan rapi. Teruskan peta tujuan dan satu atau beberapa peta sumber berdasarkan nama; fungsi melakukan iterasi pada kunci sumber dan menuliskannya ke peta tujuan.

Referensi nama memungkinkan Anda menulis satu fungsi generik merge_map yang bekerja pada semua larik asosiatif dalam skrip Anda.

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

Perluasan Tidak Langsung: Kompatibilitas dengan Bash Lama

Jika Anda harus mendukung Bash 4.0–4.2 (atau shell bawaan macOS versi lama), declare -n tidak tersedia. Alternatifnya adalah perluasan tidak langsung dengan ${!varname}.

Perluasan tidak langsung mengevaluasi nilai varname sebagai nama variabel dan mengembalikan nilai variabel tersebut. Untuk larik, Anda menggabungkannya dengan sintaks khusus untuk mendapatkan semua elemen.

Keterbatasan dibandingkan referensi nama:

  • Pada praktiknya hanya dapat dibaca (penulisan kembali memerlukan eval, yang membawa risiko injeksi)
  • Kunci larik asosiatif tidak dapat diiterasi dengan aman tanpa eval
  • Gunakan referensi nama jika memungkinkan; gunakan perluasan tidak langsung hanya jika diperlukan
#!/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"

Membangun Pustaka Skrip Modular

Dalam proyek Bash profesional, fungsi utilitas disimpan dalam berkas pustaka (misalnya, lib/array_utils.sh) dan dimuat oleh skrip yang memerlukannya.

Polanya adalah:

  • Setiap berkas pustaka hanya mendefinisikan fungsi — tanpa efek samping tingkat atas
  • Cegah pemuatan ganda dengan variabel penanda: [[ -n $__LIB_LOADED ]] && return; __LIB_LOADED=1
  • Skrip pengguna menggunakan source atau . untuk memuat pustaka
  • Fungsi bertukar data kompleks melalui referensi nama, sehingga seluruh logika tetap mandiri

Hasilnya adalah skrip yang dapat diuji, digunakan kembali, dan dipelihara — bukan trik shell sekali pakai.

Contoh Praktis: Pengurai Opsi yang Mengembalikan Peta

Contoh penggunaan di dunia nyata: pengurai opsi baris perintah yang mengisi larik asosiatif yang disediakan pemanggil. Pemanggil meneruskan nama larik sebagai argumen pertama, diikuti argumen "$@" yang sebenarnya.

Pola ini memungkinkan skrip apa pun menggunakan kembali pengurai yang sama tanpa menyalin kode atau bergantung pada variabel global.

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

Menggabungkan Referensi Nama Masukan dan Keluaran dalam Satu Fungsi

Fungsi dapat memiliki beberapa referensi nama secara bersamaan — satu untuk masukan dan satu untuk keluaran. Hal ini memungkinkan fungsi transformasi murni: fungsi membaca struktur sumber, menghitung hasil, lalu menuliskannya ke struktur tujuan, semuanya tanpa menyentuh keadaan global.

Menggunakan nama berbeda yang diawali garis bawah untuk setiap referensi nama membuat benturan tidak mungkin terjadi, bahkan ketika variabel pemanggil memiliki nama sederhana.

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

Uji Pemahaman: Referensi Nama dan Penerusan Larik

Uji pemahaman Anda tentang penerusan larik dan peta antar fungsi dalam Bash.

Rangkuman: Meneruskan Struktur Data Kompleks dalam Bash

Berikut hal-hal yang Anda pelajari dalam pelajaran ini:

  • Larik tidak dapat diteruskan berdasarkan nilai — teruskan nama variabel sebagai string.
  • declare -n ref="$varname" membuat referensi nama: alias aktif ke variabel pemanggil. Pembacaan dan penulisan sama-sama diteruskan ke variabel asli. Memerlukan Bash 4.3+.
  • Benturan referensi nama terjadi ketika variabel internal dan eksternal memiliki nama yang sama. Cegah dengan mengawali referensi nama internal (misalnya, _arr, _map).
  • Parameter keluaran bekerja dengan meneruskan nama larik/peta hasil yang kosong; fungsi mengisinya melalui referensi nama.
  • Beberapa referensi nama dalam satu fungsi memungkinkan transformasi murni dari masukan ke keluaran tanpa efek samping global.
  • Perluasan tidak langsung (${!varname[@]} + eval) adalah pilihan cadangan lama untuk Bash < 4.3, tetapi membawa risiko injeksi dan pada praktiknya hanya dapat dibaca.
  • Gabungkan teknik-teknik ini dengan berkas pustaka yang dimuat untuk membangun basis kode Bash yang modular, dapat digunakan kembali, dan dapat diuji.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Meneruskan Larik dan Peta Asosiatif antar Fungsi” gratis?

Ya — teks lengkap “Meneruskan Larik dan Peta Asosiatif antar Fungsi” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Linux Command Line & Bash Scripting Mastery, upgrade ke CoddyKit PRO. Kursus Linux Command Line & Bash Scripting Mastery mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Meneruskan Larik dan Peta Asosiatif antar Fungsi”?

Gunakan nameref dan ekspansi tidak langsung untuk meneruskan struktur data kompleks ke dalam dan ke luar fungsi dengan aman. Kamu berlatih Linux Command Line & Bash Scripting Mastery dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai Linux Command Line & Bash Scripting Mastery?

Tidak diperlukan pengalaman sebelumnya. Linux Command Line & Bash Scripting Mastery di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 4 dari 4.

Berapa lama pelajaran “Meneruskan Larik dan Peta Asosiatif antar Fungsi” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran Linux Command Line & Bash Scripting Mastery ini?

Ya. Setiap pelajaran Linux Command Line & Bash Scripting Mastery menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Merancang Fungsi dengan Lingkup Lokal dan Kode Kembalian
  2. Membangun dan Memuat Pustaka Bash yang Dapat Digunakan Ulang
  3. Mengurai Bendera dan Argumen dengan getopts
  4. Meneruskan Larik dan Peta Asosiatif antar Fungsi
← Kembali ke Linux Command Line & Bash Scripting Mastery