Fungsi

Mendefinisikan fungsi shell dengan argumen dan kode kembali.

Daftar isi

Fungsi mengelompokkan rangkaian perintah di bawah satu nama sehingga kelompok itu dapat dipanggil berulang kali tanpa disalin ulang. Topik ini membahas cara mendefinisikan fungsi, memberi argumen pada panggilannya, membatasi umur variabelnya dengan local, serta melaporkan hasil lewat kode kembali (exit code). Skrip panjang yang dirapikan menjadi kumpulan fungsi jauh lebih mudah diperiksa dan diperbaiki.

Mendefinisikan fungsi#

Fungsi didefinisikan dengan menulis nama, tanda kurung kosong, lalu badan berisi perintah di antara kurung kurawal. Definisi hanya mendaftarkan fungsi ke shell dan tidak mengeksekusi apa pun, sedangkan eksekusi terjadi setiap kali nama fungsinya dipanggil. Fungsi harus didefinisikan sebelum panggilan pertamanya, karena shell membaca skrip dari atas ke bawah.

bash luas.sh
#!/bin/bash
# fungsi menghitung luas persegi panjang

hitung_luas() {
    local panjang="$1"
    local lebar="$2"
    echo "Luas: $(( panjang * lebar ))"
}

hitung_luas 4 5
hitung_luas 12 3
text Keluaran
Luas: 20
Luas: 36

Kedua panggilan menggunakan definisi yang sama dengan argumen berbeda, dan itulah manfaat utama fungsi. Kata kunci local membatasi umur variabel panjang dan lebar pada panggilan yang sedang berjalan saja. Tanpa local, penugasan di dalam fungsi menimpa variabel global yang namanya sama, dan kekacauan semacam itu sulit dilacak pada skrip panjang.

Fungsi juga dapat didefinisikan langsung di sesi shell tanpa berkas skrip, dan perilakunya sama persis:

bash
# mendefinisikan fungsi langsung di sesi shell
sapa() {
    echo "Halo, $1"
}
sapa "Budi"
sapa "Citra"
text Keluaran
Halo, Budi
Halo, Citra

Setelah definisi ini berjalan, fungsi sapa tetap tersedia sampai sesi berakhir, sehingga dapat dipanggil lagi kapan saja. Pada berkas skrip, definisinya berlaku dari baris definisi sampai akhir berkas. bash juga menerima bentuk function nama, tetapi bentuk nama() lebih umum dan kompatibel dengan shell lain.

Argumen fungsi#

Fungsi menerima argumen seperti skrip menerima argumen, yaitu lewat $1, $2, dan seterusnya, dengan $# menyimpan banyaknya argumen dan "$@" meneruskan seluruhnya. Argumen fungsi berdiri sendiri dan tidak tercampur argumen skrip, karena $1 di dalam fungsi merujuk argumen pertama panggilan fungsi itu, bukan argumen pertama skrip.

Skrip berikut memuat satu fungsi. Seluruh argumen skrip diteruskan ke fungsi itu lewat "$@":

bash terus-argumen.sh
#!/bin/bash
# argumen skrip diteruskan ke fungsi dengan "$@"

sapa_dua() {
    echo "Argumen fungsi: $#"
    echo "Pertama: $1, kedua: $2"
}

sapa_dua "$@"
text Keluaran
Argumen fungsi: 0
Pertama: , kedua:

Dipanggil tanpa argumen, fungsi menerima nol argumen sehingga penghitungnya nol dan kedua posisinya kosong. Panggilan berikut menyiapkan folder kerja lalu meneruskan dua argumen skrip ke fungsi yang sama:

bash
# folder kerja latihan
mkdir -p /tmp/kerja-fungsi
# menyalin skrip ke folder kerja lalu masuk ke sana
cp luas.sh terus-argumen.sh genap.sh /tmp/kerja-fungsi/
cd /tmp/kerja-fungsi
# argumen skrip diteruskan ke fungsi melalui "$@"
bash terus-argumen.sh Budi Citra
text Keluaran
Argumen fungsi: 2
Pertama: Budi, kedua: Citra

Penghitung berubah menjadi 2 dan kedua posisi terisi, karena "$@" meluas menjadi seluruh argumen pemanggilan sebagai kata-kata terpisah. Apabila penerusannya dilupa, misalnya fungsi dipanggil tanpa "$@", fungsi tetap berjalan tetapi semua posisinya kosong. Kebiasaan menulis sapa_dua "$@" memastikan apa pun yang diberikan ke skrip sampai ke fungsi.

Kode kembali fungsi#

Fungsi melaporkan keberhasilannya lewat kode kembali, yaitu angka yang ditetapkan pernyataan return dengan konvensi nol untuk berhasil dan angka lain untuk gagal. Apabila fungsi berakhir tanpa return, kodenya adalah status perintah terakhir di dalam badannya. Kode kembali inilah yang dibaca oleh if ketika fungsi digunakan sebagai syarat:

bash genap.sh
#!/bin/bash
# fungsi melaporkan kegenapan lewat kode kembali

genap() {
    if [ $(( $1 % 2 )) -eq 0 ]; then
        return 0
    fi
    return 1
}

if genap 10; then
    echo "10 genap"
else
    echo "10 ganjil"
fi

if genap 7; then
    echo "7 genap"
else
    echo "7 ganjil"
fi
text Keluaran
10 genap
7 ganjil

Fungsi genap menghitung sisa bagi argumennya, lalu memanggil return 0 untuk bilangan genap dan return 1 untuk bilangan ganjil. Struktur if membaca kode tersebut tanpa perlu $?, persis seperti membaca status perintah biasa. Nilai yang sah untuk return adalah nol sampai 255, dan angka di luar rentang itu dibungkus modulo 256, sehingga return 256 justru berakhir dengan status nol. Status fungsi juga dapat dibaca langsung setelah panggilannya:

bash
# status fungsi terbaca lewat $? setelah panggilannya
cek_isi() {
    [ -n "$1" ]
}
cek_isi "teks"
echo $?
cek_isi ""
echo $?
text Keluaran
0
1

Fungsi cek_isi tidak menggunakan return sama sekali, sehingga kodenya adalah status perintah terakhirnya, yaitu [ -n "$1" ] yang memeriksa apakah argumennya tidak kosong. Panggilan dengan teks memberi status nol, sedangkan panggilan dengan teks kosong memberi status satu. Pola ini membuat fungsi pemeriksa sederhana dapat ditulis tanpa struktur if di dalamnya.

Menangkap nilai lewat substitusi#

return hanya membawa angka status, bukan nilai hasil perhitungan. Pemisahan ini membuat satu fungsi dapat melaporkan keberhasilan sekaligus mengirim nilainya lewat dua saluran berbeda. Untuk mengambil nilai dari fungsi, cetak nilainya dengan echo, lalu tangkap keluarannya dengan substitusi perintah $():

bash
# nilai hasil perhitungan ditangkap lewat $()
kali() {
    echo $(( $1 * $2 ))
}
hasil=$(kali 6 7)
echo "Enam kali tujuh: $hasil"
text Keluaran
Enam kali tujuh: 42

Seluruh keluaran fungsi masuk ke variabel hasil, sehingga pola ini menuntut fungsi mencetak nilai akhirnya saja. Pesan proses, peringatan, atau informasi lain sebaiknya dikirim ke keluaran error agar tidak tercampur ke dalam nilai yang ditangkap. Dengan dua saluran itu, fungsi shell memiliki pasangan yang rapi, yaitu kode kembali untuk keberhasilan dan keluaran standar untuk nilai.

Kesalahan umum#

  • Memanggil fungsi sebelum definisinya. Shell membaca skrip berurutan sehingga panggilan di atas definisi berakhir dengan command not found, seolah fungsi itu tidak pernah ada.
  • Mengira return mengembalikan nilai seperti pada banyak bahasa pemrograman lain. return hanya menetapkan angka status, sedangkan nilai diambil dari keluaran fungsi lewat $().
  • Melupakan local pada variabel bantu. Penugasan tanpa local menimpa variabel global yang namanya sama, dan efeknya baru terasa jauh dari tempat kesalahannya.
  • Menyisipkan echo informasi proses di dalam fungsi yang keluarannya ditangkap $(). Pesan itu ikut menjadi bagian nilai sehingga hasil tangkapannya tercampur, dan pesan semacam itu sebaiknya dikirim ke keluaran error.
  • Memakai $1 milik skrip di dalam fungsi tanpa meneruskan argumen. Fungsi memiliki ruang argumennya sendiri, sehingga posisinya tetap kosong sampai dipanggil dengan argumen atau dengan "$@".

Ringkasan#

  • Fungsi didefinisikan dengan nama() diikuti badan di antara kurung kurawal, dan wajib didefinisikan sebelum panggilan pertamanya.
  • Argumen fungsi menggunakan $1, $#, dan "$@" yang berdiri sendiri dari argumen skrip, sedangkan local menjaga variabel bantunya tetap lokal.
  • return menetapkan kode kembali dengan nol untuk berhasil, dan fungsi tanpa return mewarisi status perintah terakhirnya.
  • Nilai hasil perhitungan dikirim lewat keluaran standar dan ditangkap dengan substitusi $().

Referensi#