Percabangan

Pernyataan if, operator test, dan [[ ]] beserta pengujian keberadaan serta jenis berkas.

Daftar isi

Skrip menjadi lebih berguna ketika jalannya menyesuaikan diri dengan keadaan, misalnya memeriksa nilai sebelum menentukan predikat atau menguji keberadaan berkas sebelum membacanya. Topik ini membahas pernyataan if beserta cara bash menyusun kondisi, yaitu operator pengujian pada test, [ ], dan [[ ]], termasuk pengujian keberadaan dan jenis berkas. Keputusan sederhana dapat ditulis dalam skrip dengan struktur yang benar.

Pernyataan if#

Pernyataan if menjalankan blok perintah hanya apabila kondisinya berhasil. Kondisi tersebut adalah perintah biasa, dan bash menentukan keberhasilannya dari kode keluar, yaitu angka yang ditinggalkan setiap perintah ketika selesai. Kode keluar 0 berarti berhasil, sedangkan kode keluar selain 0 berarti gagal. Struktur paling sederhananya berbunyi if kondisi; then perintah; fi, yang dalam bentuk beberapa baris ditulis sebagai berikut.

bash
# pernyataan if menjalankan blok hanya bila kondisinya berhasil
NILAI=87
if [[ $NILAI -ge 75 ]]; then
    printf 'nilai %d dinyatakan lulus\n' "$NILAI"
fi
text Keluaran
nilai 87 dinyatakan lulus

Kondisi -ge 75 menguji apakah NILAI lebih besar atau sama dengan 75, dan karena 87 memenuhi syarat, blok di antara then dan fi dijalankan. Apabila kondisinya tidak terpenuhi, bash melewati blok itu tanpa pesan apa pun dan skrip berlanjut ke perintah setelah fi. Kata then menutup baris kondisi, sementara fi menandai akhir pernyataan if. Bentuk satu baris dengan titik koma tersebut hanya untuk merangkum strukturnya, sedangkan penulisan beberapa baris seperti contoh di atas lebih mudah dibaca dan menjadi kebiasaan yang baik.

Blok di antara then dan fi boleh memuat sejumlah perintah, termasuk pernyataan if lain yang bersarang di dalamnya. Kondisi yang dipilih juga bebas, yaitu pengujian [[ ]], perintah biasa, maupun gabungan keduanya, karena bash hanya menanyakan kode keluarnya. Cara pandang ini penting, karena seluruh keluarga percabangan bash bertumpu pada satu pertanyaan sederhana, yaitu apakah perintah kondisinya berhasil.

test, [ ], dan [[ ]]#

Tiga bentuk pengujian tersedia pada bash. Bentuk tertua adalah perintah test, yang kemudian mendapat alias [ ] agar mirip kurung, sedangkan bash menyediakan kata kunci [[ ]] sebagai perluasannya. Ketiganya menguji ekspresi dan meninggalkan kode keluar 0 apabila ekspresi benar. Perbedaan yang paling terasa ada pada variabel yang kosong atau berisi spasi.

Pemeriksaan [ ] memperlakukan hasil ekspansi sebagai kata-kata terpisah, sehingga variabel kosong yang ditulis tanpa quoting menyebabkan struktur ujiannya rusak. Demonstrasi berikut membandingkan kedua bentuk pada variabel yang sama.

bash
# uji [ ] dengan variabel kosong tanpa quoting, lalu bandingkan dengan [[ ]]
KOTA=""
[ $KOTA = "Bandung" ] || printf 'pemeriksaan [ ] tanpa quoting gagal\n'
[[ $KOTA = "Bandung" ]] || printf 'perbandingan [[ ]] berjalan lancar, hasilnya tidak sama\n'
text Keluaran
bash: line 10: [: =: unary operator expected
pemeriksaan [ ] tanpa quoting gagal
perbandingan [[ ]] berjalan lancar, hasilnya tidak sama

Baris pertama menghasilkan pesan unary operator expected karena [ ] hanya menerima satu kata di posisi kiri setelah pecah kata, yaitu tanda = yang dianggap operator tanpa perbandingannya. Tanda || setelahnya berfungsi sebagai logika atau, yaitu menjalankan perintah di kanannya hanya apabila perintah di kirinya gagal. Bentuk [[ ]] pada baris kedua tidak mengalami pecah kata, sehingga perbandingan berjalan lancar dan hasilnya memang berbeda karena nilai kosong tidak sama dengan Bandung.

Untuk skrip bash, bentuk [[ ]] adalah pilihan yang disarankan karena lebih aman terhadap nilai berisi spasi dan menyediakan operator logika di dalamnya. Bentuk [ ] tetap perlu dikenali karena banyak skrip lama menggunakannya.

Operator pengujian#

Operator pengujian dikelompokkan menurut jenis nilainya. Perbandingan angka menggunakan operator yang berbeda dari perbandingan teks, dan masing-masing memiliki arti yang tetap.

OperatorJenisArti
-eq, -neangkasama dengan, tidak sama dengan
-lt, -leangkalebih kecil, lebih kecil atau sama dengan
-gt, -geangkalebih besar, lebih besar atau sama dengan
=, !=tekssama dengan, tidak sama dengan
-z, -ntekskosong, tidak kosong
-e, -f, -dberkasada, berkas biasa, direktori

Operator angka menjalankan perbandingan aritmetika, sehingga -lt pada 9 dan 10 bernilai benar. Operator teks membandingkan isi string sepenuhnya. Demonstrasi berikut mencoba beberapa operator pada satu variabel angka.

bash
# operator aritmetika pada [[ ]]
ANGKA=10
[[ $ANGKA -eq 10 ]] && printf 'ANGKA sama dengan sepuluh\n'
[[ $ANGKA -lt 5 ]] || printf 'ANGKA tidak lebih kecil dari lima\n'
text Keluaran
ANGKA sama dengan sepuluh
ANGKA tidak lebih kecil dari lima

Tanda && berlaku sebagai logika dan, yaitu menjalankan perintah di kanannya hanya apabila perintah di kirinya berhasil. Pada baris pertama, perbandingan bernilai benar sehingga printf dijalankan, sedangkan pada baris kedua perbandingan bernilai salah sehingga perintah setelah || yang dijalankan. Dua pasangan ini menjadi cara ringkas untuk menuliskan keputusan satu baris tanpa struktur if yang lengkap.

Pengujian keberadaan dan jenis berkas#

Operator pengujian berkas memeriksa isi disk sebelum skrip membaca atau menulisnya. Operator -e menguji keberadaan tanpa melihat jenisnya, -f menguji berkas biasa, dan -d menguji direktori. Siapkan dahulu satu berkas dan satu direktori contoh, lalu uji keduanya.

bash
# siapkan berkas dan direktori untuk pengujian
printf 'satu\n' > catatan.txt
mkdir -p arsip
bash
# operator -f menguji berkas biasa, -d menguji direktori
[[ -f catatan.txt ]] && printf 'catatan.txt adalah berkas biasa\n'
[[ -d arsip ]] && printf 'arsip adalah direktori\n'
[[ -f arsip ]] || printf 'arsip bukan berkas biasa\n'
text Keluaran
catatan.txt adalah berkas biasa
arsip adalah direktori
arsip bukan berkas biasa

Baris ketiga penting untuk diamati, karena direktori arsip memang ada, tetapi tidak lolos uji -f yang khusus untuk berkas biasa. Pemilihan operator yang tepat memengaruhi jalannya skrip, misalnya pembacaan isi hanya boleh dilakukan pada berkas yang lolos uji -f, sementara penyimpanan hasil kerja sebaiknya diarahkan ke direktori yang lolos uji -d. Operator pengujian berkas lainnya, seperti -s untuk berkas yang tidak kosong dan -r untuk berkas yang dapat dibaca, mengikuti pola yang sama.

Pengujian jenis berkas menjadi pelindung pertama sebelum perintah yang berdampak dijalankan. Skrip yang menyalin atau menghapus berkas lebih aman apabila menguji -f lebih dahulu, karena perintah tersebut tidak memeriksa jenis targetnya dan langsung bekerja pada argumen yang diberikan. Kebiasaan yang sama berlaku untuk masukan pengguna, misalnya jalur berkas yang diterima skrip sebelum diolah, sebab jalur yang salah diketik tidak akan terlihat tanpa pengujian. Dengan uji keberadaan dan jenis di awal skrip, pesan kesalahan yang muncul pun menjadi lebih jelas arah penyebabnya.

if, elif, else#

Struktur if yang lengkap memiliki tiga jalur, yaitu then untuk kondisi terpenuhi, elif untuk kondisi berikutnya yang diperiksa berurutan, dan else untuk sisa kasus. Bash menghentikan pemeriksaan pada kondisi pertama yang terpenuhi, sehingga urutan kondisi menentukan hasilnya. Skrip berikut menerapkan struktur itu untuk menentukan predikat sebuah nilai ujian.

bash laporan-nilai.sh
#!/usr/bin/env bash
# laporan-nilai.sh: menentukan predikat dari sebuah nilai ujian

set -euo pipefail

NILAI=87

if [[ $NILAI -ge 85 ]]; then
    PREDIKAT="A"
elif [[ $NILAI -ge 70 ]]; then
    PREDIKAT="B"
elif [[ $NILAI -ge 60 ]]; then
    PREDIKAT="C"
else
    PREDIKAT="D"
fi

printf 'Nilai %d memperoleh predikat %s\n' "$NILAI" "$PREDIKAT"
bash
# jalankan skrip penentu predikat
bash laporan-nilai.sh
text Keluaran
Nilai 87 memperoleh predikat A

Nilai 87 lolos pada kondisi pertama, sehingga dua kondisi berikutnya tidak pernah diperiksa. Untuk melihat jalur yang lain, struktur yang sama diuji ulang dengan angka yang lebih kecil.

bash
# jalur elif dan else dengan nilai berbeda
NILAI=64
if [[ $NILAI -ge 85 ]]; then
    PREDIKAT="A"
elif [[ $NILAI -ge 70 ]]; then
    PREDIKAT="B"
else
    PREDIKAT="C"
fi
printf 'nilai %d memperoleh predikat %s\n' "$NILAI" "$PREDIKAT"
text Keluaran
nilai 64 memperoleh predikat C

Nilai 64 gagal pada dua kondisi pertama dan berlanjut ke else, yang tidak memuat kondisi apa pun dan selalu dijalankan apabila seluruh kondisi di atasnya gagal. Apabila predikat berkebalikan, misalnya rentang yang lebih kecil diuji lebih dahulu, urutan kondisi harus disusun ulang agar hasilnya tetap benar. Urutan penyusunan kondisi rentang disarankan mengikuti satu arah yang konsisten, misalnya dari tinggi ke rendah atau sebaliknya, agar skripnya mudah dibaca ulang.

Jumlah elif tidak dibatasi, sehingga klasifikasi dengan banyak tingkatan tetap berbentuk satu pernyataan if. Apabila else tidak ditulis dan tidak ada kondisi yang terpenuhi, seluruh blok dilewati tanpa pesan, yang kadang memang disengaja dan kadang menyembunyikan kasus yang terlupakan. Menulis else yang berisi pesan atau tindakan bawaan membuat kasus tidak terduga selalu terlihat. Keputusan tersebut diserahkan pada kebutuhan skrip yang sedang ditulis.

Kesalahan umum#

Kata kunci yang hilang atau salah tempat. Setiap if membutuhkan then dan ditutup dengan fi, sedangkan setiap elif membutuhkan kondisinya sendiri. Ketiadaan salah satu bagian menghasilkan pesan sintaks yang menyebut kata terakhir berkas, dan pesan tersebut sering menunjuk baris setelah akar masalahnya. Memeriksa pasangan if, then, dan fi pada penyuntingan yang menambah atau menggeser blok merupakan langkah pertama yang tepat.

Pemeriksaan [ ] tanpa quoting pada variabel yang mungkin kosong. Variabel kosong yang diekspansi tanpa kutip ganda hilang dari ekspresi sehingga struktur [ ] kehilangan salah satu sisi perbandingannya. Pesan unary operator expected pada bagian test, [ ], dan [[ ]] merupakan contohnya. Perbaikannya adalah menuliskan variabel dengan kutip ganda, atau berpindah ke [[ ]] yang tidak memecah kata.

Operator yang tidak sesuai jenisnya. Operator -eq dan kawan-kawannya hanya berlaku untuk angka, sedangkan = hanya berlaku untuk teks. Membandingkan teks dengan operator angka menghasilkan pesan error tentang ekspresi bilangan, dan membandingkan angka dengan = menghasilkan perbandingan teks yang mudah salah. Pilih operator sesuai jenis nilai yang diujikan.

Ringkasan#

  • Pernyataan if menentukan blok yang dijalankan berdasarkan kode keluar kondisinya, dengan 0 sebagai tanda keberhasilan.
  • Bentuk [[ ]] adalah pengujian bawaan bash yang aman terhadap pecah kata, sedangkan test dan [ ] adalah bentuk lama yang masih banyak digunakan.
  • Operator pengujian terbagi untuk angka, teks, dan berkas, termasuk -f untuk berkas biasa dan -d untuk direktori.
  • Struktur if, elif, dan else menyusun keputusan berlapis, dan bash berhenti pada kondisi pertama yang terpenuhi.

Referensi#