Memberi skrip kemampuan mengambil keputusan: struktur if/elif/else/fi berbasis exit status, perbedaan test [ ] (POSIX) dengan [[ ]] (bash), operator test untuk string, angka, dan file, hingga short-circuit && dan || beserta pola skrip monitoring yang siap pakai.

Setelah di episode 8 sebelumnya kita membahas operasi aritmatika — menghitung dengan $(( )) dan bc — pada episode kali ini kita menutup fondasi dasar ini dengan struktur yang membuat semua perhitungan itu berarti: pengkondisian. Aritmatika tanpa keputusan hanyalah angka; keputusan tanpa aritmatika hanya asumsi. Gabungkan keduanya, dan skrip kalian berubah dari sekadar mengeksekusi perintah menjadi berpikir.
Sejak episode 5, kalian sudah melihat sekilas banyak idiom yang akan kita bedah sekarang: [ $# -ne 2 ] untuk validasi argumen, [ -z "$nama" ] untuk cek input kosong, (( total >= 1000000 )) untuk ambang batas. Episode ini akan menyatukan semuanya: memahami mengapa struktur if di bash bekerja berdasarkan exit status, mengapa [[ ]] lebih aman daripada [ ], dan operator test untuk string, angka, dan file bisa menangkap hampir semua kondisi di dunia nyata.
if, elif, else, fi — Keputusan Berbasis Exit StatusStruktur if di bash berbeda dari bahasa pemrograman lain. Di Python atau JavaScript, if mengharapkan nilai boolean. Di bash, if mengharapkan perintah — dan ia mengambil keputusan berdasarkan exit status perintah itu: 0 berarti "benar", bukan 0 berarti "salah".
#!/usr/bin/env bash
if command_yang_dijalankan; then
echo "Berhasil (exit 0)"
elif perintah_lain; then
echo "Kondisi kedua terpenuhi"
else
echo "Semua kondisi gagal"
fiPerhatikan struktur lengkapnya: if ... then ... elif ... then ... else ... fi. fi adalah kebalikan dari if — penutup blok. Kata kunci then wajib ada setelah kondisi. Dan karena dasarnya adalah exit status, apa pun yang mengembalikan exit status bisa menjadi kondisi — bukan hanya test. Contoh paling nyata:
#!/usr/bin/env bash
if grep -q "error" /var/log/app.log; then
echo "ADA baris error di log — perlu investigasi."
else
echo "Tidak ada error ditemukan."
figrep -q mencetak apa pun (bendera -q = quiet), dan mengembalikan 0 jika pola ditemukan, 1 jika tidak. Karena if hanya membaca exit status, kalian tidak perlu menulis if [ $(grep ...) ]; then — cukup serahkan perintahnya langsung. Inilah yang membuat bash begitu elegan: kondisi adalah perintah, bukan tipe data.
if [ ... ]; then?Kalau if berbasis perintah, lalu apa itu [ ... ]? Jawabannya: [ adalah sebuah perintah — nama lain dari program test. Ia mengevaluasi ekspresi dan mengembalikan exit status. Jadi if [ "$a" = "b" ]; then dibaca sebagai: jalankan perintah test dengan argumen "$a", =, "b", lalu gunakan exit statusnya. Ini menjelaskan banyak keanehan sintaks bash — seperti kenapa spasi di dalam [ ] wajib ada:
[ "$a" = "b" ] # benar — argumen terpisah: [, "$a", =, "b", ]
[ "$a" = "b"] # salah — "b]" menjadi satu argumen, error "unexpected token"
[ "$a"="b" ] # salah — seluruh ekspresi menjadi satu argumenKarena [ adalah perintah, setiap bagian ekspresi harus menjadi argumen terpisah yang dipisahkan spasi. Ini juga alasan kenapa test dan [ bisa saling menggantikan: test "$a" = "b" sama persis dengan [ "$a" = "b" ]. Tanda ] terakhir hanyalah argumen penutup yang membuat sintaksnya terasa "seperti kurung".
[ ] vs [[ ]]: Dua Dunia BerbedaInilah salah satu keputusan desain paling penting yang harus kalian buat sebagai penulis skrip bash: memakai [ ] (test POSIX) atau [[ ]] (ekstensi bash). Keduanya terlihat mirip, tapi perilakunya berbeda fundamental.
[ ] — POSIX, Portable, Tapi Rapuh[ ] adalah perintah eksternal/internal yang mematuhi standar POSIX — artinya ia portable: bekerja di sh, dash, dan semua shell. Namun portabilitas ini datang dengan harga:
&& dan || tidak dikenali di dalamnya (mereka dipisah oleh shell sebagai operator perintah).[[ ]] — Ekstensi Bash, Lebih Kuat dan Lebih Aman[[ ]] adalah keyword bawaan bash — bukan perintah eksternal. Karena itu ia memahami sintaks dengan cara yang lebih "pintar":
&& dan || bekerja di dalam sebagai operator logika ekspresi.=~ — kalian sudah melihat sekilas di episode 6.Bandingkan:
#!/usr/bin/env bash
var="" # variabel kosong
# [ ] — Wajib kutip; tanpa kutip, error "unary operator expected"
if [ "$var" == "x" ]; then echo "x"; fi
# [[ ]] — Lebih toleran dan lebih aman
if [[ $var == "x" ]]; then echo "x"; fibash: [: x: unary operator expectedSaat $var kosong dan ditulis tanpa kutip, [ $var == "x" ] menjadi [ == "x" ] — dan test bingung karena argumen pertamanya adalah operator ==, bukan nilai. Sementara [[ $var == "x" ]] memperlakukan $var sebagai satu token apa pun isinya — tanpa perlu kutip. Inilah mengapa tim-tim skrip profesional menetapkan aturan: gunakan [[ ]] dalam skrip bash dan simpan [ ] hanya untuk skrip yang harus portable ke sh.
Important
Pilih [[ ]] untuk skrip bash (shebang #!/usr/bin/env bash) — ia lebih aman (tanpa word splitting), mendukung regex =~, dan operator &&/|| di dalamnya. Pakai [ ] hanya jika kalian menulis skrip yang benar-benar harus berjalan di shell POSIX murni (sh). Konsistensi ini menghilangkan satu kelas bug yang selama ini paling sering menimpa pemula.
&& dan || di Dalam [[ ]]Karena [[ ]] memahami operator logika, menggabungkan beberapa kondisi menjadi alami:
#!/usr/bin/env bash
user="root"
env="produksi"
if [[ $user == "root" && $env == "produksi" ]]; then
echo "Akses DIBERIKAN — kombinasi berbahaya, tetapi valid."
elif [[ $user == "root" || $env == "staging" ]]; then
echo "Akses dengan peringatan."
else
echo "Akses ditolak."
fiAkses DIBERIKAN — kombinasi berbahaya, tetapi valid.Menggunakan [ ], ekspresi yang sama butuh penulisan berantai dan kutip tambahan: [ "$user" = "root" ] && [ "$env" = "produksi" ]. Keduanya bekerja, tapi [[ ]] jauh lebih mudah dibaca — dan keterbacaan adalah fitur, bukan kemewahan.
Sekarang kita masuk ke perangkat keras dari pengkondisian: operator-operator test. Ketiganya bekerja sama di [ ] dan [[ ]] (dengan sedikit perbedaan yang sudah dibahas).
#!/usr/bin/env bash
nama="Arman"
if [[ -z "$nama" ]]; then echo "String kosong."; fi
if [[ -n "$nama" ]]; then echo "String tidak kosong."; fi
if [[ "$nama" == "Arman" ]]; then echo "Sama persis."; fi
if [[ "$nama" != "Budi" ]]; then echo "Berbeda dengan 'Budi'."; fiString tidak kosong.
Sama persis.
Berbeda dengan 'Budi'.| Operator | Benar jika... | Kontra-intuitif? |
|---|---|---|
-z "$var" | string kosong (zero-length) | Ya — -z = "nol panjang", bukan "ada nilai" |
-n "$var" | string tidak kosong | Ya — kebalikan intuitif dari -z |
$a == $b | dua string sama | Tidak |
$a != $b | dua string berbeda | Tidak |
Untuk angka, jangan memakai > atau < di dalam [ ] — mereka adalah operator redirection shell, bukan perbandingan! Bash menyediakan operator kata-kata khusus:
#!/usr/bin/env bash
pemakaian=87
if (( pemakaian >= 90 )); then
echo "KRITIS: pemakaian $pemakaian%."
elif (( pemakaian >= 80 )); then
echo "PERINGATAN: pemakaian $pemakaian%."
else
echo "Normal: pemakaian $pemakaian%."
fi| Operator | Arti |
|---|---|
-eq | equal — sama dengan |
-ne | not equal — tidak sama |
-gt | greater than — lebih besar |
-ge | greater or equal — lebih besar atau sama |
-lt | less than — lebih kecil |
-le | less or equal — lebih kecil atau sama |
Perhatikan contoh di atas menggunakan (( )) dari episode 8 — ketika hanya membandingkan integer, (( )) adalah pilihan yang lebih bersih daripada [ "$pemakaian" -ge 90 ]. Tapi kalian akan sering menemui bentuk [ "$x" -ge 90 ] di skrip lain, jadi menguasai kedua sintaks itu penting. Dan bila nilai bisa pecahan (dari bc), ingat pelajaran episode 8: biarkan bc yang membandingkan.
Inilah yang membuat bash hebat untuk otomasi: bash bisa menanyakan properties file langsung, tanpa perlu memanggil program lain.
#!/usr/bin/env bash
file="/etc/nginx/nginx.conf"
if [[ -f "$file" ]]; then
echo "Ada file: $file"
elif [[ -d "$file" ]]; then
echo "Ini direktori, bukan file biasa."
fi
if [[ -r "$file" ]]; then
echo "File bisa dibaca."
fi| Operator | Benar jika... | Skenario Nyata |
|---|---|---|
-e path | path ada (file, dir, symlink, apa pun) | Cek keberadaan sebelum akses |
-f path | path adalah file biasa | -f = "file" — kunci konfigurasi |
-d path | path adalah direktori | Cek sebelum cd atau loop glob |
-r path | file bisa dibaca | Validasi izin sebelum membaca |
-w path | file bisa ditulis | Cek sebelum menulis log |
-x path | file bisa dieksekusi | Cek biner/skrip sebelum menjalankan |
-s path | file ada dan tidak kosong | -s = "size > 0" — log yang kosong |
Tip
Banyak pemula memakai -e dan -f secara bergantian — padahal berbeda. -e benar untuk path apa pun (termasuk symlink dan direktori); -f khusus file biasa. Saat kalian menulis if [ -f "$dir" ] untuk variabel yang sebenarnya direktori, kondisi itu akan selalu salah. Tanyakan pada diri sendiri: apa yang persis ingin kuverifikasi? lalu pilih operator yang sesuai — presisi ini yang membuat skrip bisa dipercaya.
&& dan ||Selain if, bash punya cara ringkas untuk pengambilan keputusan: short-circuit evaluation. Pada pipeline, cmd1 && cmd2 menjalankan cmd2 hanya jika cmd1 sukses; cmd1 || cmd2 menjalankan cmd2 hanya jika cmd1 gagal.
#!/usr/bin/env bash
mkdir -p /tmp/backup && echo "Direktori dibuat."
[ -d /tmp/backup ] || echo "Direktori tidak ada — buat dulu!"Direktori dibuat.
Direktori tidak ada — buat dulu!Mengapa ini disebut short-circuit? Karena bash berhenti mengevaluasi begitu hasilnya sudah pasti. Pada A && B, jika A gagal, bash tidak perlu menjalankan B (hasil sudah pasti salah). Pada A || B, jika A sukses, B dilewati (hasil sudah pasti benar). Inilah mengapa idiom ini efisien — dan kenapa urutan operan sangat menentukan.
&& / || dan Kapan if?Kedua gaya ini bisa menulis ulang satu sama lain, tapi punya konteks terbaiknya:
#!/usr/bin/env bash
# Ringkas — untuk aksi satu baris: "kalau berhasil, lakukan ini"
systemctl start nginx && echo "nginx berjalan"
# Eksplisit — untuk logika bercabang yang butuh penjelasan
if systemctl start nginx; then
echo "nginx berjalan"
else
echo "Gagal memulai nginx" >&2
exit 1
fiAturan praktisnya: &&/|| untuk aksi beruntun yang pendek dan jelas; if untuk logika yang bercabang, ber-komentar, atau berperilaku berbeda saat gagal. Skrip produksi lebih sering butuh if — karena saat kegagalan terjadi, kita hampir selalu perlu melakukan lebih dari sekadar mencetak satu baris (misalnya exit 1 dan log). Namun ada satu pengecualian wajib yang harus selalu diingat — lihat pitfall nomor 4 di bawah.
Saatnya merangkai semua operator test menjadi skrip monitoring yang realistis — gabungan dari episode 5 (argumen), episode 8 (aritmatika), dan episode ini (pengkondisian):
#!/usr/bin/env bash
set -euo pipefail
threshold="${1:-80}"
log_file="/var/log/cek_disk.log"
if ! [[ "$threshold" =~ ^[0-9]+$ ]]; then
echo "Error: ambang batas harus angka." >&2
exit 1
fi
pemakaian=$(df --output=pcent / | tail -1 | tr -d ' %')
if [[ -f "$log_file" ]]; then
if [[ -w "$log_file" ]]; then
echo "[$(date +%F)] pemakaian=$pemakaian%" >> "$log_file"
else
echo "PERINGATAN: $log_file tidak bisa ditulis." >&2
fi
fi
if (( pemakaian >= threshold )); then
echo "KRITIS: pemakaian disk ${pemakaian}% melebihi ambang ${threshold}%."
exit 2
else
echo "OK: pemakaian disk ${pemakaian}% di bawah ambang ${threshold}%."
exit 0
fi./cek_disk.sh 90OK: pemakaian disk 37% di bawah ambang 90%.Mari bedah bagaimana setiap jenis test berkontribusi:
| Baris | Test yang Dipakai | Tujuan |
|---|---|---|
${1:-80} | Parameter expansion dengan default | Skrip jalan tanpa argumen, ambang default 80 |
[[ "$threshold" =~ ^[0-9]+$ ]] | String regex dengan ! (negasi) | Menolak ambang yang bukan angka murni |
[[ -f "$log_file" ]] | File: apakah file log ada | Jangan meng-appended ke file yang belum ada |
[[ -w "$log_file" ]] | File: apakah bisa ditulis | Peringatkan alih-alih gagal diam-diam |
(( pemakaian >= threshold )) | Angka dengan (( )) | Keputusan utama: melewati ambang atau tidak |
Perhatikan bagaimana ketiga jenis test (string, file, angka) bekerja berdampingan untuk satu tujuan. Skrip ini bisa dipasang di cron untuk berjalan tiap 5 menit — dan karena exit 0/exit 2 digunakan, penelepon (misalnya sistem monitoring) bisa membedakan "semua baik" dari "ada masalah" hanya dari exit status. Inilah kekuatan pengkondisian yang dirancang dengan kesadaran penuh.
[ ] hilang[ "$a" = "b" ] benar; [ "$a" = "b"] error "missing ]"; [ "$a"="b" ] error "unary operator expected". Karena [ adalah perintah, setiap token harus dipisah spasi. Ini bukan sekadar gaya — ini sintaks.
> / < sebagai perbandingan angkaDi dalam [ ] dan [[ ]], > dan < adalah perbandingan string (urutan abjad), bukan angka! [[ "9" > "10" ]] bernilai benar karena "9" secara leksikografis lebih besar dari "1". Untuk angka, gunakan -gt, -lt, dst. (atau (( ))).
[ ][ $var == "x" ] dengan $var kosong menjadi [ == "x" ] → error "unary operator expected". Di [ ], kutip selalu. Di [[ ]], kutip menjadi opsional (tapi tetap disarankan).
cmd1 && cmd2 || cmd3 — pola tiga arah yang bahayaIni pola yang menggoda tapi berbahaya: jika cmd2 sendiri gagal, bash akan menjalankan cmd3 juga — karena cmd1 && cmd2 menghasilkan "salah" saat cmd2 gagal, lalu || cmd3 ikut tereksekusi. Hasilnya, jalur "sukses" bisa tetap memicu cabang error. Jangan menulis logika tiga arah dengan &&/|| — gunakan if/else yang tidak ambigu.
[ $var == "x" ] vs [[ $var == "x" ]] — perbedaan nyataSelain masalah kutip, ada jebakan halus lain: dalam [[ ]], == dan = sama-sama perbandingan string. Dalam [ ] POSIX, operator yang resmi adalah = — == mungkin bekerja di bash, tapi di sh/dash perilakunya tidak dijamin. Jika skrip harus portable, tulis [ "$var" = "x" ]. Jika bash-only, gunakan [[ ]].
; atau then di baris yang samaif [ ... ] then — tanpa ; sebelum then — error. Dua gaya valid: if [ ... ]; then (satu baris) atau if [ ... ] lalu baris baru then. Pilih satu dan konsisten.
Pada episode 9 ini, kita telah melengkapi fondasi pengambilan keputusan: struktur if/elif/else/fi yang bekerja berbasis exit status perintah; perbedaan fundamental [ ] (perintah POSIX yang rapuh terhadap word splitting) vs [[ ]] (keyword bash yang lebih aman, mendukung regex dan operator logika); katalog test operator untuk string (-z, -n, ==, !=), angka (-eq, -ne, -gt, -ge, -lt, -le), dan file (-f, -d, -e, -r, -w, -x, -s); serta short-circuit &&/|| — lengkap dengan skrip monitoring disk yang memadukan semuanya.
Inti yang harus dibawa pulang:
if membaca exit status, bukan boolean — kondisi adalah perintah.[[ ]] untuk skrip bash: lebih aman, regex, &&/|| di dalam.-z -n == !=; Angka: -eq -ne -gt -ge -lt -le; File: -f -d -e -r -w -x -s.>/< untuk angka — itu perbandingan string.[ ]; hindari pola &&/|| tiga arah.Kalian kini memiliki empat fondasi inti bash: data (argumen & input), daftar file (globbing), perhitungan (aritmatika), dan keputusan (pengkondisian). Kombinasinya sudah bisa membangun skrip otomasi yang nyata. Langkah berikutnya adalah memperhalus pengambilan keputusan itu sendiri. Di episode 10 selanjutnya, kita akan membahas Pengkondisian Lanjutan (Case Statement & Regex) — mencocokkan satu variabel terhadap banyak pola sekaligus dengan case ... esac, serta memvalidasi input seperti email, IP address, dan angka menggunakan regular expression dengan operator =~. Sampai jumpa di episode berikutnya!