Mereka Bentuk Fungsi dengan Skop Setempat dan Kod Pulangan
Tulis fungsi yang menggunakan pemboleh ubah setempat, status keluar dan nilai pulangan berasaskan printf, bukannya global yang rapuh.
Mereka Bentuk Fungsi dengan Skop Setempat dan Kod Pulangan ialah pelajaran Kem Intensif DevOps percuma di CoddyKit. Ini ialah pelajaran 1 daripada 4. Sebanyak 3 pelajaran dalam laluan pembelajaran ini boleh dibaca sepenuhnya secara percuma — selepas itu, CoddyKit PRO membuka akses kepada semua pelajaran, serta latihan praktikal dengan penyunting kod terbina dalam dan tutor kecerdasan buatan yang tersedia 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Kem Intensif DevOps, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Kem Intensif DevOps merangkumi sejumlah 4 pelajaran.
Mengapa Skop Fungsi Penting
Dalam Bash, pemboleh ubah secara lalai adalah global. Pemboleh ubah yang ditetapkan dalam fungsi akan bocor ke skop pemanggil melainkan anda mengisytiharkannya secara jelas sebagai local. Ini merupakan punca biasa pepijat halus dalam skrip shell.
- Fungsi tanpa
localboleh menulis ganti pemboleh ubah pemanggil secara senyap. - Pemboleh ubah
localdimusnahkan apabila fungsi kembali. - Sempadan skop yang jelas menjadikan fungsi boleh digunakan semula dan boleh diuji secara berasingan.
Pelajaran ini mengajar anda menulis fungsi yang serba lengkap: fungsi tersebut menggunakan pemboleh ubah local, menyampaikan hasil melalui kod keluar dan printf, serta tidak pernah bergantung pada keadaan global tersirat.
Masalah Kebocoran Global
Berikut ialah contoh nyata kebocoran pemboleh ubah global. Fungsi set_name menetapkan pemboleh ubah bernama result, yang secara senyap-senyap menimpa pemboleh ubah result milik pemanggil sendiri.
Jalankan skrip ini dan perhatikan output yang tidak dijangka — result milik pemanggil hilang selepas panggilan fungsi.
#!/usr/bin/env bash
set_name() {
result="Alice" # No 'local' — this is GLOBAL
}
result="important data"
echo "Before: $result"
set_name
echo "After: $result" # Prints 'Alice', not 'important data'Mengisytiharkan Pemboleh Ubah Tempatan dengan 'local'
Perintah terbina dalam local mengehadkan skop pemboleh ubah kepada fungsi yang mengandungi pemboleh ubah itu serta mana-mana fungsi yang dipanggilnya. Di luar fungsi tersebut, pemboleh ubah itu sama ada tidak ditetapkan atau mengekalkan nilai sebelumnya.
local varname— mengisytiharkan tanpa memberikan nilai.local varname="value"— mengisytiharkan dan memberikan nilai dalam satu langkah.local -i count=0— mengisytiharkan pemboleh ubah tempatan bertipe integer.local -r PI=3.14159— mengisytiharkan pemalar tempatan baca sahaja.
Amalan terbaik: isytiharkan setiap pemboleh ubah dalam fungsi sebagai local kecuali jika anda memang memerlukannya sebagai pemboleh ubah global.
#!/usr/bin/env bash
greet() {
local name="$1" # local — safe
local greeting="Hello, ${name}!"
echo "$greeting"
} # 'name' and 'greeting' vanish here
name="global value"
greet "Bob"
echo "name is still: $name" # Prints 'global value'Kod Keluar sebagai Nilai Pulangan
Fungsi Bash tidak boleh return rentetan — return hanya menetapkan status keluar integer (0–255). Mengikut kebiasaan:
return 0— berjayareturn 1(atau sebarang nilai bukan sifar) — gagal
Pemanggil membaca status keluar melalui $? sejurus selepas panggilan, atau menggunakan fungsi itu secara langsung dalam syarat if. Kod keluar ialah cara lazim untuk menandakan kejayaan atau kegagalan daripada sesuatu fungsi.
#!/usr/bin/env bash
is_even() {
local -i n="$1"
(( n % 2 == 0 )) # arithmetic command: exits 0 if true, 1 if false
}
for num in 2 3 7 10; do
if is_even "$num"; then
echo "$num is even"
else
echo "$num is odd"
fi
doneMenyampaikan Hasil Rentetan melalui printf
Apabila fungsi perlu mengembalikan hasil rentetan, corak piawai ialah mencetak ke stdout dan menangkapnya dengan penggantian perintah $(). Penggunaan printf berbanding echo lebih digalakkan kerana:
printftidak menambahkan baris baharu di hujung secara lalai (kecuali anda menyertakan\n).- Tingkah laku
printfadalah konsisten dan ditakrifkan oleh POSIX;echoberbeza-beza mengikut shell. - Penggantian perintah membuang baris baharu di hujung, jadi
printf '%s' "$value"adalah tepat.
#!/usr/bin/env bash
to_uppercase() {
local input="$1"
printf '%s' "${input^^}" # Bash 4+ parameter expansion
}
word="hello"
upper=$(to_uppercase "$word")
echo "Original: $word"
echo "Upper: $upper"Menggabungkan Kod Keluar dan Output stdout
Fungsi yang direka dengan baik boleh mencetak hasil (apabila berjaya) dan menandakan kegagalan (melalui kod keluar) pada masa yang sama. Pemanggil menentukan tindakan berdasarkan kod keluar sebelum mempercayai output tersebut.
Corak di bawah digunakan secara meluas dalam pustaka Bash sebenar:
- Apabila berjaya: gunakan
printfuntuk mencetak hasil danreturn 0. - Apabila gagal: tulis diagnostik ke stderr (bukan stdout) dan gunakan
return 1. - Menulis ralat ke stderr memastikan stdout kekal bersih untuk saluran paip.
#!/usr/bin/env bash
divide() {
local -i numerator="$1"
local -i denominator="$2"
if (( denominator == 0 )); then
printf 'Error: division by zero\n' >&2
return 1
fi
printf '%d' $(( numerator / denominator ))
return 0
}
if result=$(divide 20 4); then
echo "20 / 4 = $result"
else
echo "Division failed."
fi
if result=$(divide 10 0); then
echo "10 / 0 = $result"
else
echo "Division failed (caught the error)."
fiMenggunakan 'local' untuk Melindungi Fungsi Rekursif
Rekursi ialah salah satu contoh paling jelas tentang sebab local sangat penting. Setiap panggilan rekursif mendapat salinan berasingan bagi setiap pemboleh ubah local pada tindanan panggilan. Tanpa local, setiap panggilan akan menimpa pemboleh ubah global yang sama dan menghasilkan keputusan yang salah.
Fungsi faktorial di bawah selamat kerana n dan sub adalah tempatan kepada setiap bingkai tindanan.
#!/usr/bin/env bash
factorial() {
local -i n="$1"
local -i sub
if (( n <= 1 )); then
printf '1'
return 0
fi
sub=$(factorial $(( n - 1 )))
printf '%d' $(( n * sub ))
}
for i in 1 2 3 4 5 6; do
echo "${i}! = $(factorial $i)"
doneMengelakkan Perangkap Subshell dengan local -n (Nameref)
Penggantian perintah $() dijalankan dalam subshell. Sebarang tugasan pemboleh ubah di dalamnya tidak kelihatan oleh shell induk. Apabila anda perlu membolehkan fungsi menulis ke dalam pemboleh ubah yang diberikan oleh pemanggil tanpa subshell, gunakan nameref (local -n), yang tersedia dalam Bash 4.3 dan versi lebih baharu.
local -n ref="$1"menjadikanrefsebagai alias untuk pemboleh ubah yang namanya disimpan dalam$1.- Memberikan nilai kepada
refdi dalam fungsi mengubah pemboleh ubah pemanggil secara langsung. - Ini mengelakkan subshell sambil mengekalkan butiran pelaksanaan secara tempatan.
#!/usr/bin/env bash
# Fills caller's array by reference — no subshell needed
read_csv_line() {
local -n _out="$1" # nameref to caller's variable
local line="$2"
local IFS=','
read -ra _out <<< "$line"
}
declare -a fields
read_csv_line fields "alice,30,engineer"
echo "Name: ${fields[0]}"
echo "Age: ${fields[1]}"
echo "Role: ${fields[2]}"Membina Pustaka Fungsi Kecil
Projek Bash dunia sebenar membahagikan fungsi yang boleh digunakan semula kepada fail pustaka yang dimuatkan oleh skrip dengan source (atau operator titik .). Peraturan reka bentuk pustaka yang baik:
- Setiap pemboleh ubah dalam fungsi pustaka mestilah
local. - Fungsi pustaka tidak pernah menggunakan
exit— fungsi tersebut menggunakanreturn, supaya pemanggil terus berjalan. - Gunakan awalan ruang nama yang konsisten (contohnya,
str_,log_) untuk mengelakkan pertembungan nama. - Lindungi daripada pemuatan sumber berganda dengan pemboleh ubah penanda.
Di bawah ialah pustaka utiliti rentetan minimum yang mengikuti konvensyen ini.
#!/usr/bin/env bash
# lib/str.sh — string utility library
[[ -n "${_LIB_STR_LOADED:-}" ]] && return 0
_LIB_STR_LOADED=1
str_trim() {
local str="$1"
str="${str#"${str%%[![:space:]]*}"}"
str="${str%"${str##*[![:space:]]}"}"
printf '%s' "$str"
}
str_repeat() {
local -i times="$2"
local char="$1"
local -i i
for (( i = 0; i < times; i++ )); do
printf '%s' "$char"
done
}
str_contains() {
local haystack="$1"
local needle="$2"
[[ "$haystack" == *"$needle"* ]]
}
# --- self-test when executed directly ---
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
trimmed=$(str_trim " hello world ")
echo "Trimmed: '${trimmed}'"
str_repeat '-' 20; echo
if str_contains "bash scripting" "script"; then
echo "Contains: yes"
fi
fiMengesahkan Argumen dalam Fungsi
Fungsi yang menerima argumen hendaklah mengesahkannya lebih awal dan mengembalikan kod keluar khusus apabila input tidak sah. Ini dipanggil corak klausa pengawal — gagal dengan cepat dan jelas.
- Semak bilangan argumen dengan
$#. - Sahkan jenis atau format sebelum melakukan sebarang kerja.
- Cetak mesej diagnostik ke stderr sahaja, jangan sekali-kali ke stdout.
- Gunakan kod pulangan bukan sifar yang berbeza (contohnya, 1 = argumen salah, 2 = fail tidak ditemui) supaya pemanggil boleh bertindak balas secara berbeza terhadap mod kegagalan yang berlainan.
#!/usr/bin/env bash
file_line_count() {
if (( $# != 1 )); then
printf 'Usage: file_line_count <file>\n' >&2
return 1
fi
local file="$1"
if [[ ! -f "$file" ]]; then
printf 'Error: not a file: %s\n' "$file" >&2
return 2
fi
if [[ ! -r "$file" ]]; then
printf 'Error: cannot read: %s\n' "$file" >&2
return 3
fi
local -i count
count=$(wc -l < "$file")
printf '%d' "$count"
return 0
}
# Test with /etc/hosts (exists on every Linux/macOS system)
if lines=$(file_line_count /etc/hosts); then
echo "/etc/hosts has $lines lines"
else
echo "Failed with exit code: $?"
fiMenggabungkan Semuanya: Contoh Dunia Sebenar
Berikut ialah skrip lengkap dan kendiri yang menunjukkan semua konsep daripada pelajaran ini berfungsi bersama:
- Pemboleh ubah
localdalam setiap fungsi. - Kod keluar untuk menandakan kejayaan atau kegagalan.
printfuntuk menyampaikan hasil rentetan.- Ralat ditulis ke stderr dan hasil ke stdout.
- Klausa pengawal untuk pengesahan argumen.
Kaji alirannya: parse_version mengekstrak data, version_ge membandingkannya, dan main menggunakan kedua-duanya dengan kemas.
#!/usr/bin/env bash
# Parse a semver string into components via nameref
parse_version() {
local -n _major="$2" _minor="$3" _patch="$4"
local version="$1"
local IFS='.'
local -a parts
read -ra parts <<< "$version"
_major="${parts[0]:-0}"
_minor="${parts[1]:-0}"
_patch="${parts[2]:-0}"
}
# Return 0 if version $1 >= version $2
version_ge() {
local -i maj_a min_a pat_a
local -i maj_b min_b pat_b
parse_version "$1" maj_a min_a pat_a
parse_version "$2" maj_b min_b pat_b
if (( maj_a != maj_b )); then (( maj_a > maj_b ))
elif (( min_a != min_b )); then (( min_a > min_b ))
else (( pat_a >= pat_b ))
fi
}
require_bash_version() {
local required="$1"
local actual="${BASH_VERSION%%(*}"
if version_ge "$actual" "$required"; then
printf 'Bash %s satisfies >= %s\n' "$actual" "$required"
return 0
else
printf 'Error: need Bash >= %s, got %s\n' "$required" "$actual" >&2
return 1
fi
}
main() {
require_bash_version "4.3" || return 1
require_bash_version "99.0" || true # demonstrates failure path
}
mainSemakan Pengetahuan: Pemboleh Ubah Tempatan dan Nilai Pulangan
Pertimbangkan fungsi Bash berikut. Apakah cara yang betul untuk menangkap hasil rentetannya dalam pemanggil, dan pernyataan manakah tentang pemboleh ubah tmp yang benar?
transform() {
local tmp="${1,,}" # lowercase
printf '%s' "$tmp"
return 0
}Imbas Kembali: Fungsi dengan Skop Tempatan dan Kod Pulangan
Dalam pelajaran ini, anda telah mempelajari cara menulis fungsi Bash yang kemas, boleh digabungkan dan selamat:
- Sentiasa gunakan
localuntuk pemboleh ubah dalam fungsi bagi mengelakkan pencemaran skop pemanggil. - Gunakan kod keluar (
return 0/1/N) untuk menandakan kejayaan atau kegagalan — kod ini berintegrasi secara semula jadi denganif,&&dan||. - Gunakan
printfke stdout untuk menyampaikan hasil rentetan; tangkap hasil tersebut dengan$()dalam pemanggil. - Tulis ralat ke stderr (
>&2) supaya stdout kekal bersih untuk aliran data dan saluran paip. - Gunakan
local -n(nameref) apabila anda perlu menulis ke dalam pemboleh ubah yang diberikan oleh pemanggil tanpa kos tambahan subshell. - Klausa pengawal (sahkan argumen lebih awal, pulangkan serta-merta apabila input tidak sah) menjadikan fungsi kukuh dan menerangkan perilakunya sendiri.
- Fail pustaka hendaklah dimuatkan dengan source, menggunakan awalan ruang nama, tidak pernah memanggil
exit, dan dilindungi daripada pemuatan sumber berganda.
Menguasai corak ini membezakan skrip sekali guna yang rapuh daripada pangkalan kod Bash profesional yang mudah diselenggara.
Pelajari Kem Intensif DevOps dengan tutor kecerdasan buatan — percuma
Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.
- Kursus
- 142
- Pelajaran
- 568
Soalan Lazim
Adakah pelajaran “Mereka Bentuk Fungsi dengan Skop Setempat dan Kod Pulangan” percuma?
Ya — sebanyak 3 pelajaran dalam laluan pembelajaran Kem Intensif DevOps, termasuk “Mereka Bentuk Fungsi dengan Skop Setempat dan Kod Pulangan”, boleh dibaca sepenuhnya secara percuma di web ini. Selepas itu, CoddyKit PRO membuka akses kepada semua pelajaran, serta latihan interaktif dengan penyunting kod terbina dalam dan tutor kecerdasan buatan yang tersedia 24/7. Kursus Kem Intensif DevOps merangkumi sejumlah 4 pelajaran.
Apakah yang akan saya pelajari dalam “Mereka Bentuk Fungsi dengan Skop Setempat dan Kod Pulangan”?
Tulis fungsi yang menggunakan pemboleh ubah setempat, status keluar dan nilai pulangan berasaskan printf, bukannya global yang rapuh. Anda berlatih Kem Intensif DevOps menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.
Adakah saya memerlukan pengalaman untuk memulakan Kem Intensif DevOps?
Tiada pengalaman terdahulu diperlukan. Pembelajaran Kem Intensif DevOps di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 1 daripada 4.
Berapa lamakah pelajaran “Mereka Bentuk Fungsi dengan Skop Setempat dan Kod Pulangan” diambil?
Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.
Bolehkah saya menulis dan menjalankan kod dalam pelajaran Kem Intensif DevOps ini?
Ya. Setiap pelajaran Kem Intensif DevOps menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.
Semua pelajaran dalam kursus ini
- Mereka Bentuk Fungsi dengan Skop Setempat dan Kod Pulangan
- Membina dan Memanggil Pustaka Bash Boleh Guna Semula
- Menghurai Bendera dan Argumen dengan getopts
- Menghantar Tatasusunan dan Peta Bersekutu antara Fungsi