Percabangan & Kondisi

Percabangan & Kondisi

Bitnesia • Aug 30, 2026 76 •EN

Script yang sudah kita tulis sejak Bab 1 sampai Bab 5 masih berjalan lurus dari atas ke bawah, setiap baris perintah dieksekusi begitu saja tanpa peduli kondisi sistem saat itu. Kenyataan di lapangan tidak sesederhana itu. Sysadmin perlu memeriksa dulu apakah sebuah service benar-benar berjalan sebelum mengirim notifikasi bahwa service itu down, User perlu memastikan file sumber ada sebelum menyalinnya, dan Developer perlu membedakan environment staging dari production sebelum menjalankan perintah deploy. Bab ini membahas percabangan, yaitu kemampuan script untuk mengambil keputusan dan menjalankan blok kode yang berbeda-beda tergantung kondisi yang terpenuhi. Kita akan mulai dari struktur if/elif/else beserta dua bentuk operator kondisi ([ ] dan [[ ]]), lanjut ke berbagai operator pembanding untuk angka, string, dan file, masuk ke case statement untuk menyederhanakan banyak kondisi sekaligus, dan ditutup dengan exit status serta operator logika && dan || yang jadi fondasi pengambilan keputusan di Bash.

6.1 if, elif, else, dan Operator Kondisi

Sub-bab ini membahas struktur percabangan paling dasar di Bash, yaitu if, beserta dua bentuk operator yang dipakai untuk menguji kondisinya.

6.1.1 Struktur Dasar if

Struktur if menjalankan sebuah blok perintah hanya jika kondisi yang diuji terpenuhi. Bentuk paling sederhananya memakai then untuk menandai awal blok dan fi (kebalikan ejaan if) untuk menutupnya.

if [ -d "/etc/nginx" ]; then
    echo "Direktori konfigurasi nginx ditemukan"
fi

Detail yang penting dipahami sejak awal: if sebenarnya tidak mengevaluasi nilai boolean seperti di bahasa pemrograman lain. if menjalankan sebuah perintah, dalam contoh di atas perintahnya adalah [ -d "/etc/nginx" ], lalu memeriksa exit status perintah itu. Jika exit status bernilai 0 (sukses), blok then dijalankan. Jika tidak, blok itu dilewati. Konsep exit status ini akan dibahas tuntas di sub-bab 6.4, tapi penting diingat sejak sekarang karena inilah mekanisme sesungguhnya di balik setiap percabangan Bash.

Titik koma sebelum then pada contoh di atas boleh diganti baris baru, gaya penulisan yang juga lazim dipakai terutama untuk kondisi yang panjang.

if [ -d "/etc/nginx" ]
then
    echo "Direktori konfigurasi nginx ditemukan"
fi

6.1.2 Menambahkan elif dan else

else menjalankan blok alternatif saat kondisi if tidak terpenuhi, sedangkan elif (singkatan "else if") menambahkan kondisi susulan tanpa perlu menutup dan membuka if baru secara terpisah.

disk_usage=92

if [ "$disk_usage" -ge 90 ]; then
    echo "Kritis: pemakaian disk sudah $disk_usage%"
elif [ "$disk_usage" -ge 75 ]; then
    echo "Peringatan: pemakaian disk sudah $disk_usage%"
else
    echo "Pemakaian disk masih aman: $disk_usage%"
fi

Bash mengevaluasi kondisi if dan setiap elif secara berurutan dari atas ke bawah, dan langsung berhenti begitu menemukan kondisi pertama yang terpenuhi, sisanya diabaikan. Jumlah elif tidak dibatasi, tapi di lapangan, rantai elif yang terlalu panjang untuk memeriksa banyak nilai dari satu variabel yang sama biasanya lebih pas ditulis dengan case, topik yang dibahas di sub-bab 6.3.

6.1.3 Test Builtin [ ] dan Keyword [[ ]]

Bash menyediakan dua cara menulis kondisi: [ ] dan [[ ]]. Keduanya sering terlihat mirip, tapi berbeda secara fundamental di level shell.

[ ] adalah bentuk lain dari perintah test, sebuah builtin Bash yang juga tersedia sebagai perintah eksternal (biasanya di /usr/bin/test) untuk kompatibilitas dengan shell POSIX seperti sh atau dash. Karena statusnya sebagai perintah biasa, [ ] tunduk pada aturan parsing shell yang sama seperti perintah lain, termasuk word splitting dan pathname expansion pada variabel yang tidak dikutip (aturan yang sudah dibahas di Bab 3.4).

nama=""
if [ $nama = "budi" ]; then
    echo "Cocok"
fi

Baris di atas akan gagal dengan pesan error seperti [: =: unary operator expected karena variabel $nama yang kosong dan tidak dikutip membuat perintah test menerima argumen yang tidak lengkap. Ini salah satu jebakan paling umum bagi pemula.

[[ ]] adalah keyword Bash (bukan perintah biasa), fitur tambahan yang juga tersedia di ksh tapi bukan bagian dari standar POSIX shell. Karena parser Bash memprosesnya langsung, bukan lewat aturan parsing perintah biasa, [[ ]] kebal terhadap word splitting dan pathname expansion pada variabel yang tidak dikutip di dalamnya.

nama=""
if [[ $nama = "budi" ]]; then
    echo "Cocok"
else
    echo "Tidak cocok"
fi

Baris ini berjalan aman meski $nama kosong dan tidak dikutip, karena [[ ]] tetap memperlakukannya sebagai satu argumen tunggal (dalam kasus ini string kosong), bukan argumen yang hilang.

6.1.4 Kapan Memilih [[ ]] daripada [ ]

Aturan praktis yang berlaku luas di kalangan penulis script Bash, termasuk yang direkomendasikan ShellCheck: gunakan [[ ]] secara default untuk script yang memang ditulis khusus untuk Bash (shebang #!/bin/bash, sesuai Bab 1.4), karena lebih aman terhadap variabel tidak dikutip dan mendukung fitur tambahan seperti pattern matching serta regular expression dengan =~. Gunakan [ ] hanya jika script memang harus portable ke shell POSIX murni seperti sh atau dash, misalnya script init sistem lama atau target lingkungan yang tidak menjamin ketersediaan Bash. Satu catatan penting: [[ ]] tidak bisa dipakai di dalam script dengan shebang #!/bin/sh pada sistem yang sh-nya bukan Bash (misalnya Debian dan turunannya yang memakai dash sebagai /bin/sh), karena keyword ini murni fitur Bash.

6.1.5 Troubleshooting: Kesalahan Sintaks if/then/fi

Dua kesalahan sintaks berikut ini paling sering muncul saat baru terbiasa menulis if, dan keduanya menghasilkan pesan error yang cukup membingungkan bagi pemula karena tidak langsung menunjuk baris yang sebenarnya salah. Kesalahan pertama adalah lupa menuliskan then pada baris yang sama dengan kondisi (dipisah titik koma) atau di baris berikutnya secara terpisah.

if [ -d "/etc/nginx" ]
    echo "Direktori ditemukan"
fi
bash: syntax error near unexpected token `fi'

Pesan error di atas terlihat aneh karena justru menuding baris fi, padahal akar masalahnya ada di baris pertama yang tidak diikuti then. Ini terjadi karena Bash menganggap [ -d "/etc/nginx" ] dan echo "Direktori ditemukan" sebagai dua perintah biasa yang masih menjadi bagian dari kondisi if, terus menunggu kata kunci then muncul, dan baru melaporkan error begitu menemukan fi tanpa pernah menjumpai then yang diharapkan. Kesalahan kedua adalah lupa menutup struktur dengan fi sama sekali, yang membuat Bash menganggap script belum selesai meski sudah mencapai baris terakhir.

bash: syntax error: unexpected end of file

Kedua error ini gejalanya serupa: pesan yang muncul menunjuk ke lokasi yang jauh dari baris asal masalah. Kebiasaan yang membantu mencegahnya adalah langsung mengetik pasangan if...fi (atau then setelah kondisi) sekaligus sebelum mengisi baris-baris di antaranya, alih-alih menulis dari atas ke bawah lalu lupa menutupnya.

6.2 Membandingkan Angka, String, dan File

Sub-bab ini membahas operator pembanding yang dipakai di dalam [ ] maupun [[ ]], dikelompokkan berdasarkan jenis data yang dibandingkan: angka, string, dan atribut file.

6.2.1 Operator Perbandingan Angka

Test builtin memakai operator berbasis huruf untuk perbandingan angka, bukan simbol seperti == atau >, karena simbol-simbol itu sudah dipakai untuk keperluan lain (perbandingan string dan redirection).

OperatorArti
-eqsama dengan (equal)
-netidak sama dengan (not equal)
-gtlebih besar (greater than)
-gelebih besar atau sama dengan (greater or equal)
-ltlebih kecil (less than)
-lelebih kecil atau sama dengan (less or equal)
jumlah_proses=15

if [ "$jumlah_proses" -gt 10 ]; then
    echo "Jumlah proses di atas ambang batas"
fi

Operator-operator ini hanya bekerja untuk bilangan bulat (integer), tidak mendukung angka desimal. Membandingkan 3.5 -gt 3 akan menghasilkan error integer expression expected, karena test builtin tidak tahu cara menafsirkan titik desimal sebagai bagian dari angka. Untuk perbandingan angka desimal, kita perlu tool eksternal seperti awk atau bc, topik yang dibahas lebih lanjut di Bab 11. Sebagai alternatif untuk perbandingan bilangan bulat, Bash juga menyediakan arithmetic evaluation dengan (( )) yang sudah disinggung sekilas di Bab 3.3, dan mendukung simbol matematika biasa seperti >, <, dan ==.

if (( jumlah_proses > 10 )); then
    echo "Jumlah proses di atas ambang batas"
fi

Kesalahan umum pemula adalah memakai == untuk membandingkan angka di dalam [ ] atau [[ ]], padahal == adalah operator perbandingan string, bukan angka. Kebetulan "10" == "10" tetap menghasilkan hasil yang benar secara string, tapi "010" == "10" akan dianggap tidak sama karena dibandingkan sebagai teks literal, bukan nilai numerik.

6.2.2 Operator Perbandingan String

Perbandingan string memakai simbol, bukan operator berbasis huruf seperti pada angka.

OperatorArti
= atau ==string sama persis
!=string tidak sama
-zstring kosong (panjang nol)
-nstring tidak kosong
<lebih kecil secara leksikografis (urutan kamus)
>lebih besar secara leksikografis
status_service="aktif"

if [ "$status_service" = "aktif" ]; then
    echo "Service sedang berjalan"
fi

if [ -z "$status_service" ]; then
    echo "Status service belum diketahui"
fi

Perlu diperhatikan, di dalam [ ], operator == sebenarnya perluasan Bash yang tidak dijamin ada di semua implementasi test POSIX, jadi = tunggal lebih portable jika target script memang shell POSIX murni. Di dalam [[ ]], keduanya setara dan sama-sama aman dipakai.

Operator < dan > di dalam [ ] harus di-escape dengan backslash (\< dan \>), karena tanpa escape, shell akan menafsirkannya sebagai operator redirection yang sudah dibahas di Bab 5.3, bukan sebagai bagian dari perintah test.

if [ "abc" \< "abd" ]; then
    echo "abc lebih dulu secara urutan kamus"
fi

Di dalam [[ ]], escape ini tidak diperlukan karena parser Bash sudah tahu keduanya berada di dalam konteks kondisi, bukan redirection.

if [[ "abc" < "abd" ]]; then
    echo "abc lebih dulu secara urutan kamus"
fi

Satu perbedaan penting yang perlu diketahui: di dalam [[ ]], sisi kanan operator == atau != yang tidak dikutip diperlakukan sebagai glob pattern, bukan string literal. Artinya [[ "app.log" == *.log ]] bernilai benar karena *.log dianggap pola, bukan teks harfiah. Ini berguna untuk pencocokan pola sederhana, tapi bisa jadi jebakan jika kita justru ingin membandingkan string apa adanya. Kutip sisi kanan dengan tanda kutip jika memang ingin perbandingan literal, misalnya [[ "$nama_file" == "*.log" ]].

6.2.3 Operator Pengujian File

Operator pengujian file memeriksa keberadaan dan atribut sebuah file atau direktori di filesystem, sangat sering dipakai Sysadmin sebelum melakukan operasi yang bergantung pada file tersebut.

OperatorArti
-epath ada, apa pun jenisnya (file, direktori, dll.)
-fpath ada dan berupa regular file
-dpath ada dan berupa direktori
-rpath ada dan bisa dibaca (readable)
-wpath ada dan bisa ditulis (writable)
-xpath ada dan bisa dieksekusi (executable)
-spath ada dan ukurannya lebih besar dari nol
-Lpath ada dan berupa symbolic link
file_konfigurasi="/etc/app/config.conf"

if [ -f "$file_konfigurasi" ] && [ -r "$file_konfigurasi" ]; then
    echo "File konfigurasi ditemukan dan bisa dibaca"
else
    echo "File konfigurasi tidak ditemukan atau tidak bisa dibaca"
fi

Kesalahan yang sering ditemui di lapangan adalah memakai -e saat sebenarnya bermaksud memastikan sebuah path adalah file biasa. -e tetap bernilai benar meski path itu ternyata direktori, sehingga operasi berikutnya seperti membaca isi file dengan cat bisa gagal dengan pesan error yang membingungkan. Gunakan -f secara spesifik jika memang menyasar regular file, dan -d jika menyasar direktori.

6.2.4 Troubleshooting: Jebakan Unquoted Variable

Kesalahan paling umum di seluruh sub-bab ini adalah lupa mengutip variabel di dalam [ ]. Selain kasus string kosong yang sudah dibahas di sub-bab 6.1.3, variabel yang mengandung spasi juga bisa merusak logika percabangan.

nama_file="laporan bulanan.txt"

if [ -f $nama_file ]; then
    echo "File ditemukan"
fi

Baris di atas akan gagal dengan error too many arguments, karena $nama_file yang tidak dikutip mengalami word splitting menjadi dua argumen terpisah (laporan dan bulanan.txt), padahal -f hanya mengharapkan satu argumen path. Solusinya konsisten dengan yang sudah dibahas di Bab 3.4: selalu kutip variabel dengan tanda kutip ganda, [ -f "$nama_file" ]. Kebiasaan ini tetap dianjurkan meski memakai [[ ]] yang sebenarnya kebal terhadap word splitting, supaya konsisten dan tidak menimbulkan kebingungan saat berpindah antara [ ] dan [[ ]] di script yang sama.

6.3 case Statement untuk Banyak Kondisi

Sub-bab ini membahas case, struktur percabangan yang lebih ringkas dibanding rantai if/elif yang panjang ketika kita perlu memeriksa satu nilai terhadap banyak kemungkinan.

6.3.1 Sintaks Dasar case

case statement mencocokkan satu nilai terhadap serangkaian pola, lalu menjalankan blok perintah pada pola pertama yang cocok. Strukturnya diakhiri kata kunci esac, kebalikan ejaan case.

read -rp "Pilih aksi (start/stop/status): " aksi

case "$aksi" in
    start)
        echo "Menjalankan service..."
        ;;
    stop)
        echo "Menghentikan service..."
        ;;
    status)
        echo "Memeriksa status service..."
        ;;
    *)
        echo "Aksi tidak dikenali: $aksi"
        ;;
esac

Tanda ;; menutup setiap blok pola, menandai akhir blok itu sekaligus mengeluarkan eksekusi dari keseluruhan case, tanpa memeriksa pola-pola sesudahnya. Pola *) di baris terakhir berfungsi sebagai default case, cocok dengan nilai apa pun yang tidak tertangkap pola sebelumnya, mirip peran else pada if.

6.3.2 Pattern Matching dan Gabungan Pola

Pola pada case memakai aturan glob pattern yang sama seperti pencocokan nama file di shell (bukan regular expression), dengan * mencocokkan string apa pun, ? mencocokkan satu karakter, dan [...] mencocokkan satu karakter dari sekumpulan karakter tertentu.

nama_file="laporan.log"

case "$nama_file" in
    *.txt)
        echo "File teks biasa"
        ;;
    *.log)
        echo "File log"
        ;;
    backup_*)
        echo "File hasil backup"
        ;;
    *)
        echo "Jenis file tidak dikenali"
        ;;
esac

Beberapa pola bisa digabung dalam satu blok memakai tanda | sebagai pemisah, berguna saat beberapa nilai berbeda memang harus diperlakukan sama.

case "$aksi" in
    -h|--help)
        echo "Penggunaan: script.sh [start|stop|status]"
        ;;
    start|restart)
        echo "Menjalankan (ulang) service..."
        ;;
esac

Pola semacam -h|--help ini sangat umum dipakai saat menangani argumen script, topik yang dibahas lebih lengkap di Bab 12.

6.3.3 Fall-Through dengan ;& dan ;;&

Selain ;;, Bash sejak versi 4.0 menyediakan dua terminator tambahan untuk kebutuhan yang lebih spesifik: ;& dan ;;&. Karena fitur ini butuh Bash 4 ke atas, pastikan versi Bash target sudah memenuhi syarat (cek dengan bash --version, dibahas di Bab 1.3) sebelum memakainya di script yang mungkin dijalankan di lingkungan lama.

;& membuat eksekusi "jatuh" ke blok berikutnya secara paksa, tanpa memeriksa pola blok itu, mirip fall-through pada switch di beberapa bahasa pemrograman lain.

level=2

case "$level" in
    1)
        echo "Level 1"
        ;&
    2)
        echo "Level 2"
        ;&
    3)
        echo "Level 3"
        ;;
esac

Karena level bernilai 2, output di atas menampilkan Level 2 lalu ikut menampilkan Level 3 juga, sebab ;& pada blok 2) memaksa eksekusi lanjut ke blok 3) tanpa mencocokkan polanya terlebih dahulu.

;;& berbeda tipis: setelah menjalankan blok yang cocok, Bash tetap melanjutkan pencocokan pola-pola berikutnya (bukan langsung menjalankan blok selanjutnya begitu saja), dan bisa menjalankan lebih dari satu blok jika beberapa pola sama-sama cocok.

nama_file="laporan_backup.log"

case "$nama_file" in
    *.log)
        echo "Ini file log"
        ;;&
    backup_*|*backup*)
        echo "Ini file backup"
        ;;
esac

Output di atas menampilkan kedua baris karena nama_file cocok dengan pola *.log maupun pola *backup*, dan ;;& pada blok pertama membuat Bash tetap memeriksa pola kedua alih-alih berhenti. Dua terminator ini jarang dipakai untuk kasus sehari-hari, tapi berguna saat sebuah nilai memang perlu memicu beberapa aksi sekaligus berdasarkan kategori yang tidak saling eksklusif.

6.3.4 Troubleshooting: Urutan Pola dan Variabel di Dalam Pola

Kesalahan yang sering luput dari perhatian pada case adalah urutan penulisan pola. Bash mencocokkan pola secara berurutan dari atas ke bawah dan langsung menjalankan blok pertama yang cocok, tanpa peduli apakah ada pola lain di bawahnya yang sebenarnya lebih spesifik.

aksi="start"

case "$aksi" in
    *)
        echo "Aksi tidak dikenali"
        ;;
    start)
        echo "Menjalankan service..."
        ;;
esac

Script di atas selalu menampilkan Aksi tidak dikenali meski aksi bernilai start, karena pola *) yang diletakkan di baris pertama sudah mencocokkan segala kemungkinan nilai lebih dulu, sehingga blok start) di bawahnya tidak akan pernah tercapai. Aturan praktis yang wajib diingat: pola paling spesifik harus diletakkan lebih dulu, dan pola *) sebagai penangkap segala kemungkinan harus selalu berada paling akhir.

Jebakan kedua yang lebih halus muncul saat sebuah variabel dipakai sebagai bagian dari pola, bukan sebagai nilai yang diuji. Variabel yang tidak dikutip di posisi pola tetap diperlakukan sebagai glob, sehingga karakter seperti * atau ? di dalam nilainya ikut berfungsi sebagai wildcard, bukan teks literal.

prefix="backup*"
nama_file="backup_harian.log"

case "$nama_file" in
    $prefix)
        echo "Cocok sebagai pola"
        ;;
    *)
        echo "Tidak cocok"
        ;;
esac

Baris di atas menampilkan Cocok sebagai pola, karena tanda * di dalam $prefix tetap berfungsi sebagai wildcard saat tidak dikutip. Jika yang diinginkan justru pencocokan literal terhadap teks backup* apa adanya (termasuk tanda bintangnya), kutip variabel itu di posisi pola, misalnya "$prefix"), supaya karakter di dalamnya kehilangan makna khusus sebagai glob.

6.4 Exit Status dan Operator Logika

Sub-bab penutup ini membahas exit status secara lebih mendalam, konsep yang sudah disinggung berkali-kali sejak sub-bab 6.1 karena memang menjadi dasar seluruh mekanisme percabangan Bash, sekaligus dua operator logika yang memakainya secara langsung.

6.4.1 Variabel $? dan Konvensi Exit Status

Setiap perintah yang selesai dijalankan di Bash meninggalkan sebuah exit status, yaitu angka yang menunjukkan apakah perintah itu berhasil atau gagal. Konvensi yang berlaku universal di seluruh sistem Unix-like: 0 berarti sukses, sedangkan angka bukan nol (1 sampai 255) berarti gagal, dengan makna spesifik dari tiap angka biasanya bergantung pada perintah yang mengembalikannya. Variabel bawaan $? menyimpan exit status dari perintah yang paling terakhir selesai dijalankan.

grep -q "error" /var/log/app.log
echo "Exit status grep: $?"

grep mengembalikan 0 jika pola ditemukan, dan 1 jika tidak ditemukan sama sekali, itulah kenapa grep sangat sering dipakai langsung sebagai kondisi if tanpa perlu menampung outputnya ke variabel dulu (opsi -q di atas membuat grep tidak menampilkan apa pun ke layar, hanya mengembalikan exit status).

if grep -q "error" /var/log/app.log; then
    echo "Ditemukan baris error di log"
fi

Perlu diperhatikan, $? selalu merujuk pada perintah yang benar-benar terakhir dieksekusi, jadi nilainya gampang tertimpa tanpa sengaja. Simpan segera ke variabel lain jika nilainya masih dibutuhkan setelah menjalankan perintah lain, termasuk perintah sesederhana echo untuk menampilkan pesan.

./deploy.sh
status_deploy=$?
echo "Proses deploy sedang diperiksa..."

if [ "$status_deploy" -ne 0 ]; then
    echo "Deploy gagal dengan exit status $status_deploy" >&2
fi

Bash membatasi angka exit status pada rentang 8 bit, yaitu 0 sampai 255. Jika sebuah perintah atau script keluar dengan exit memakai angka di luar rentang itu, Bash memotongnya ke rentang tersebut (modulo 256), sehingga exit 256 pada praktiknya menghasilkan exit status 0, persis sama seperti sukses, meski niatnya mungkin menandai kegagalan. Kebiasaan yang aman adalah selalu memakai angka 1 sampai 255 secara eksplisit untuk menandai kegagalan, dan sebisa mungkin dikonsistenkan artinya di seluruh script.

6.4.2 Operator && dan || untuk Short-Circuit

&& menjalankan perintah kedua hanya jika perintah pertama sukses (exit status 0), sedangkan || menjalankan perintah kedua hanya jika perintah pertama gagal (exit status bukan 0). Perilaku ini disebut short-circuit evaluation, karena Bash tidak perlu mengevaluasi perintah kedua sama sekali kalau hasil keseluruhan sudah bisa dipastikan dari perintah pertama.

mkdir -p /tmp/backup_harian && echo "Direktori siap dipakai"

Operator || berguna untuk memberi respons balik saat sebuah perintah gagal, misalnya mengecek konektivitas ke server lain (kombinasi redirection dari Bab 5.3 dengan operator logika di sini).

ping -c 1 server1.example.com > /dev/null 2>&1 || echo "Server1 tidak bisa dijangkau"

Kedua operator ini bisa dirangkai untuk pola "jika berhasil lakukan A, jika gagal lakukan B", pola yang sangat umum dipakai Sysadmin untuk validasi cepat sebelum menjalankan operasi berikutnya.

cd /var/www/app && git pull || echo "Gagal masuk direktori atau menarik update"

Satu jebakan yang perlu diwaspadai: jika perintah pertama pada pola A && B sendiri bisa gagal karena alasan yang berbeda-beda, rangkaian || di ujung tidak bisa membedakan apakah yang gagal itu A atau B. Untuk validasi yang butuh kejelasan lebih, struktur if biasa yang sudah dibahas di sub-bab 6.1 lebih mudah dibaca dan di-debug dibanding merangkai terlalu banyak && dan || dalam satu baris.

Sebagai catatan tambahan, test builtin [ ] sebenarnya juga mengenal operator logika -a (AND) dan -o (OR) untuk menggabungkan dua kondisi dalam satu tanda kurung siku, tapi POSIX sudah menandai keduanya sebagai obsolescent dan sebaiknya dihindari untuk script baru. Gunakan && atau || untuk menggabungkan dua blok [ ] atau [[ ]] yang terpisah sebagai gantinya.

# Hindari pola lama ini
if [ -f "$file" -a -r "$file" ]; then
    echo "File ada dan bisa dibaca"
fi

# Lebih disarankan
if [ -f "$file" ] && [ -r "$file" ]; then
    echo "File ada dan bisa dibaca"
fi

6.4.3 Troubleshooting: $? Tertimpa Tanpa Sengaja

Karena $? hanya merekam exit status dari perintah yang benar-benar paling terakhir dijalankan (seperti sudah disinggung di sub-bab 6.4.1), kesalahan yang sering luput adalah menyisipkan perintah lain, sekecil apa pun, di antara perintah yang ingin diperiksa dan pemeriksaan $? itu sendiri.

./deploy.sh
echo "Memeriksa hasil deploy..."

if [ "$?" -eq 0 ]; then
    echo "Deploy berhasil"
else
    echo "Deploy gagal" >&2
fi

Script di atas nyaris selalu melaporkan Deploy berhasil, apa pun hasil sebenarnya dari ./deploy.sh, karena baris echo "Memeriksa hasil deploy..." ikut berjalan sebelum $? diperiksa, dan echo hampir selalu sukses (exit status 0) sehingga menimpa exit status ./deploy.sh yang sebenarnya ingin diuji. Solusinya konsisten dengan yang sudah dibahas di sub-bab 6.4.1: tampung $? ke variabel lain segera setelah perintah yang ingin diperiksa selesai, sebelum menjalankan perintah apa pun yang lain, termasuk echo sekalipun.

./deploy.sh
status_deploy=$?
echo "Memeriksa hasil deploy..."

if [ "$status_deploy" -eq 0 ]; then
    echo "Deploy berhasil"
else
    echo "Deploy gagal" >&2
fi

6.4.4 Menggabungkan if, case, dan Exit Status dalam Script

Sebagai penutup bab, mari kita rangkai if, case, operator pembanding file, dan exit status dalam satu script kecil yang mengecek kesiapan sebuah direktori proyek sebelum menjalankan aksi tertentu, skenario sederhana yang mewakili pola validasi yang akan terus berulang di bab-bab berikutnya.

#!/bin/bash

direktori_proyek="/var/www/app"
aksi="${1:-status}"

if [ ! -d "$direktori_proyek" ]; then
    echo "Error: direktori proyek $direktori_proyek tidak ditemukan" >&2
    exit 1
fi

case "$aksi" in
    status)
        if [ -f "$direktori_proyek/.git/HEAD" ]; then
            echo "Proyek ditemukan dan merupakan repository Git"
        else
            echo "Proyek ditemukan, tapi bukan repository Git"
        fi
        ;;
    deploy)
        cd "$direktori_proyek" && git pull
        if [ "$?" -eq 0 ]; then
            echo "Deploy berhasil"
        else
            echo "Deploy gagal" >&2
            exit 1
        fi
        ;;
    *)
        echo "Aksi tidak dikenali: $aksi. Gunakan 'status' atau 'deploy'." >&2
        exit 2
        ;;
esac

Perhatikan tanda seru pada [ ! -d "$direktori_proyek" ] di baris awal, tanda ini membalik hasil pengujian, sehingga blok if dijalankan justru saat direktori tidak ditemukan. Script langsung keluar dengan exit 1 pada kondisi ini, mencegah case di bawahnya sempat dieksekusi dengan direktori yang tidak valid. Simpan sebagai cek_proyek.sh, beri izin eksekusi, lalu jalankan dengan argumen aksi yang diinginkan.

chmod +x cek_proyek.sh
./cek_proyek.sh status
./cek_proyek.sh deploy
./cek_proyek.sh restart

Jalankan juga echo $? segera setelah tiap pemanggilan untuk melihat sendiri bagaimana exit status berbeda tergantung hasilnya, sukses bernilai 0, aksi tidak dikenal bernilai 2, dan kegagalan proses deploy bernilai 1. Membedakan exit status semacam ini berguna saat script dipanggil dari script lain atau dari cron di Bab 18, karena pemanggilnya bisa mengambil keputusan lanjutan berdasarkan angka spesifik itu, bukan cuma tahu "gagal" secara umum. Pola if untuk validasi awal, case untuk menangani banyak aksi, dan exit status yang berbeda-beda untuk tiap jenis kegagalan seperti ini akan terus dipakai berulang, termasuk saat masuk ke perulangan di Bab 7 dan penanganan argumen script yang lebih formal di Bab 12.