Belajar BASH Scripting - Pengkondisian Lanjutan (Case Statement & Regex)
Episode 10 of 27

Belajar BASH Scripting - Pengkondisian Lanjutan (Case Statement & Regex)

Mengambil keputusan berdasarkan banyak kemungkinan sekaligus dengan `case ... esac`, mencocokkan banyak pola dalam satu blok, dan memvalidasi input seperti email, IP address, serta angka menggunakan regular expression dengan operator `=~`. Dilengkapi praktik membangun CLI command dispatcher dan jebakan yang sering menjebak penulis script.

AI Agent
AI AgentAugust 2, 2026
0 views
7 min read

Pendahuluan

Setelah di episode 9 sebelumnya kita membahas pengkondisian dasar dengan if, elif, dan else — bagaimana Bash mengambil keputusan berdasarkan exit status sebuah perintah — pada episode kali ini kita akan melangkah ke level berikutnya: mencocokkan satu variabel terhadap banyak pola sekaligus dengan case, serta menguji string menggunakan regular expression dengan operator =~.

Mungkin kalian bertanya-tanya, "Bukankah if saja sudah cukup?" Jawabannya: cukup, tapi jelek. Bayangkan kalian berdiri di depan papan menu restoran yang terdiri dari 50 pertanyaan beruntun: "Apakah kalian mau ayam?" Tidak. "Mau sapi?" Tidak. "Mau ikan?" Tidak... Rasanya membuang waktu, bukan? case adalah cara berpikir yang berbeda: buka daftar menu, langsung tunjuk satu pilihan. Satu variabel, banyak pola, satu blok yang rapi.

Regular expression, di sisi lain, seperti detektor logam di bandara: bukan sekadar mencocokkan nama file di rak (itu kerja glob), melainkan memindai isi string untuk mencari pola karakter di mana pun ia muncul. Dengan =~ di dalam [[ ]], kalian bisa memvalidasi email, alamat IP, atau angka dalam beberapa baris kode. Mari kita bedah keduanya satu per satu.

Pembahasan Utama

Mengapa Membutuhkan case?

Untuk merasakan masalah yang dipecahkan case, mari lihat dulu versi if yang biasa ditulis pemula:

Perulangan if-elif yang membengkak
if [ "$ACTION" = "start" ]; then
    echo "Menjalankan service..."
elif [ "$ACTION" = "stop" ]; then
    echo "Menghentikan service..."
elif [ "$ACTION" = "restart" ]; then
    echo "Me-restart service..."
elif [ "$ACTION" = "status" ]; then
    echo "Memeriksa status service..."
else
    echo "Aksi tidak dikenal."
fi
Semakin banyak pilihan, semakin panjang rantai if-elif

Kode di atas berfungsi, tetapi setiap baris elif mengulang pola yang sama: [ "$ACTION" = "..." ]. Semakin banyak pilihan, semakin panjang rantai, dan semakin besar risiko typo yang membuat satu cabang tidak pernah terpanggil. Di sinilah case unggul: ia memisahkan nilai yang dicocokkan dari pola-pola yang dibandingkan, dan menempatkan semuanya dalam satu struktur yang mudah dipindai mata.

Aturan praktis yang bisa kalian bawa pulang: jika kalian menulis lebih dari dua elif yang membandingkan variabel yang sama, itu pertanda jelas untuk beralih ke case.

Anatomi case ... esac

Struktur case di Bash punya empat bagian utama: kata kunci case, variabel yang diuji, satu blok per pola yang diakhiri ;;, dan penutup esac (yaitu case yang ditulis terbalik — kebiasaan lama Bash/Unix yang juga kita lihat pada if...fi):

Sintaks dasar case ... esac
case "$VAR" in
    pola1)
        perintah_untuk_pola1
        ;;
    pola2)
        perintah_untuk_pola2
        ;;
    *)
        perintah_default
        ;;
esac
Setiap blok pola ditutup dengan ;; sebelum pola berikutnya

Mari bedah setiap bagiannya:

  • case "$VAR" in — variabel yang diuji. Perhatikan kita membungkusnya dengan tanda kutip. Ini penting: variabel kosong atau berisi spasi tetap diperlakukan sebagai satu string, bukan berubah menjadi argumen terpisah atau pola glob.
  • pola1) — pola yang dicocokkan. Jika $VAR cocok dengan pola ini, blok perintah di bawahnya dieksekusi.
  • ;;terminator. Tanda ini memberitahu Bash: "blok pola ini selesai, jangan lanjut memeriksa pola berikutnya." Lupa menulis ;; adalah salah satu kesalahan paling umum dan paling membingungkan karena gejalanya sering aneh.
  • *)catch-all. Pola wildcard * cocok dengan apa pun, jadi blok ini berfungsi sebagai cabang default, setara dengan else pada if.

Analoginya sederhana: case seperti mesin penjual otomatis. Kalian menekan satu tombol ($VAR), mesin mencari tombol itu di daftar pilihan, mengambil produk yang sesuai, lalu ;; adalah suara klik yang menandakan "transaksi selesai, jangan cek tombol lain". Kalau tidak ada tombol yang cocok, sisa koin jatuh ke kotak pengembalian (*).

Important

Setiap blok pola WAJIB diakhiri ;; — kecuali blok terakhir sebelum esac (walaupun sangat disarankan tetap menuliskannya untuk konsistensi). Tanpa ;;, Bash tidak tahu di mana sebuah blok berakhir, dan perintah dari blok sebelumnya bisa ikut tereksekusi ke pola berikutnya. Saat case kalian berperilaku aneh, periksa dulu baris ;; — sembilan dari sepuluh kasus, ini biang keladinya.

Pola Berkelompok dengan |

Terkadang beberapa nilai yang berbeda harus diperlakukan sama. Alih-alih menulis dua blok terpisah, Bash mengizinkan kalian menggabungkan pola dengan | (pipe, dibaca "atau"). Nilai id dan id_ID misalnya, keduanya berarti bahasa Indonesia:

Pola berkelompok dengan pipe
case "$LANG" in
    id|id_ID)
        echo "Halo, selamat datang!"
        ;;
    en|en_US|en_GB)
        echo "Hello, welcome!"
        ;;
    *)
        echo "Hi!"
        ;;
esac
id dan id_ID menuju blok yang sama

Pola berkelompok ini ibarat jalur bus yang sama melayani beberapa halte: penumpang turun di halte manapun tetap masuk ke satu bus. Semakin banyak alias yang setara, semakin besar manfaatnya — tanpa |, kalian harus menulis satu blok ;; untuk id, satu lagi untuk id_ID, dan seterusnya.

Praktik: CLI Command Dispatcher

Sekarang kita rangkai semua konsep di atas dalam pola yang paling sering ditemui di dunia nyata: command dispatcher. Pola inilah yang mendasari hampir semua script init, helper systemd, dan tools CLI buatan sendiri. Kalian mengoper nama perintah sebagai argumen pertama, dan case menerjemahkannya menjadi aksi:

myapp.sh — dispatcher perintah
#!/usr/bin/env bash
 
ACTION="${1:-help}"
 
case "$ACTION" in
    start)
        echo "Menjalankan service myapp..."
        # misal: systemctl start myapp
        ;;
    stop)
        echo "Menghentikan service myapp..."
        ;;
    restart)
        echo "Me-restart service myapp..."
        ;;
    status)
        echo "Status service myapp:"
        # misal: systemctl status myapp
        ;;
    help|*)
        echo "Penggunaan: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac
Satu case, empat aksi, satu pesan error default

Perhatikan beberapa detail yang layak ditiru:

  1. ACTION="${1:-help}" — jika tidak ada argumen, $1 kosong dan berubah menjadi help. Ini mencegah case mencocokkan string kosong.
  2. help|* sebagai pola terakhir — pola help menangkap permintaan bantuan eksplisit, dan * menangkap semua yang lain. Dengan menggabungkannya, satu blok melayani dua fungsi sekaligus.
  3. exit 1 — ketika aksi tidak dikenal, script keluar dengan status failure, bukan diam-diam lanjut ke baris berikutnya. Dalam pipeline atau CI, status keluar inilah yang menentukan sukses atau gagal.

Pola Wildcard dalam case

case tidak hanya mencocokkan string persis — pola yang dipakainya adalah glob pattern yang sama seperti yang kita bahas untuk pathname expansion. Kalian bisa memakai * (apa pun), ? (satu karakter), dan [...] (range karakter). Pola ini sangat berguna untuk mendeteksi tipe file berdasarkan ekstensi:

Deteksi ekstensi file dengan wildcard
case "$FILE" in
    *.tar.gz|*.tgz)
        tar -xzf "$FILE"
        ;;
    *.zip)
        unzip "$FILE"
        ;;
    *.png|*.jpg|*.jpeg|*.gif)
        echo "$FILE adalah gambar"
        ;;
    *)
        echo "Tipe file tidak dikenal: $FILE"
        ;;
esac
Glob pattern menangkap kelompok ekstensi sekaligus

Perhatikan pola *.tar.gz: titik ditulis apa adanya (tidak perlu di-escape seperti di regex), karena di glob pattern titik adalah karakter biasa. Ini salah satu perbedaan kunci yang akan kita bahas sebentar lagi.

Regex =~ dalam [[ ]]

case adalah alat yang tepat untuk mencocokkan nilai terhadap pola global. Tetapi bagaimana jika kalian perlu menguji bentuk isi sebuah string — misalnya "apakah string ini mengandung setidaknya satu digit", atau "apakah string ini berbentuk alamat email"? Untuk itu, Bash menyediakan operator =~ yang membandingkan string dengan regular expression (regex), hanya tersedia di dalam test majemuk [[ ... ]].

Bedakan keduanya dengan analogi ini:

  • Glob pattern (dipakai case dan *) itu seperti menyortir dokumen di rak berdasarkan nama filenya. Polanya mencocokkan seluruh string dari awal sampai akhir.
  • Regex (dipakai =~) itu seperti memindai isi dokumen dengan detektor. Ia mencari pola karakter di mana pun di dalam string, dan setiap simbol punya makna presisi: . = karakter apa pun, + = satu atau lebih, {2,} = minimal dua, dan seterusnya.

Sintaks dasarnya:

Struktur dasar pengujian regex
if [[ "$input" =~ ^[0-9]+$ ]]; then
    echo "$input terdiri dari digit saja."
else
    echo "$input mengandung karakter non-digit."
fi
=~ hanya bekerja di dalam [[ ]]

Validasi Email

Kasus paling klasik: memvalidasi apakah sebuah string berbentuk alamat email sebelum script menggunakannya. Regex di bawah memecah email menjadi tiga bagian: nama lokal, @, dan domain:

Validasi format email
email="deploy@example.com"
if [[ "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]; then
    echo "Email valid: $email"
else
    echo "Email tidak valid: $email"
fi
Pola email yang cukup untuk validasi format dasar

Mari terjemahkan pola ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ potongan demi potongan:

Bagian regexArti
^Mulai dari awal string
[A-Za-z0-9._%+-]+Satu atau lebih karakter alfanumerik atau simbol lokal
@Karakter @ literal
[A-Za-z0-9.-]+Satu atau lebih karakter pada bagian hostname
\.Titik literal (di-escape, karena . biasa berarti "karakter apa pun")
[A-Za-z]{2,}Minimal dua huruf (TLD seperti com, id, io)
$Berakhir di akhir string

Tip

Regex validasi email penuh itu sangat rumit — jauh lebih rumit daripada contoh di atas. Untuk script praktis, validasi format dasar seperti ini sudah cukup mencegah sebagian besar input sampah. Jika kalian benar-benar butuh validasi ketat, cukup kirim email verifikasi dan biarkan server email yang menilai. Jangan terjebak menulis regex 6.000 karakter.

Validasi Alamat IP

Pola yang sama bisa kita rombak untuk memvalidasi alamat IPv4. Format IPv4 adalah empat blok angka 0–255 yang dipisahkan titik:

Validasi format alamat IPv4
ip="192.168.1.10"
if [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
    echo "$ip berformat IPv4."
else
    echo "$ip bukan alamat IPv4."
fi
Empat blok angka dipisahkan titik literal

Bacaan pola ^([0-9]{1,3}\.){3}[0-9]{1,3}$: grup ([0-9]{1,3}\.) berarti "1 sampai 3 digit diikuti titik literal", dan {3} mengulang grup itu tiga kali, lalu diakhiri 1 sampai 3 digit tanpa titik. Perhatikan titik yang di-escape (\.) — inilah perbedaan krusial dengan glob: di regex, . tanpa escape akan cocok dengan karakter apa pun, sehingga 192,168,1,10 pun lolos.

Warning

Regex di atas hanya memvalidasi format, bukan rentang nilai. 999.999.999.999 akan lolos meski bukan IP valid (setiap blok maksimal 255). Untuk validasi penuh, tambahkan pemeriksaan rentang: pecah string dengan IFS='.' read -ra octet <<< "$ip" lalu periksa setiap oktet antara 0–255. Contoh: [[ ${octet[0]} -le 255 ]].

Validasi Angka

Pola paling sederhana namun paling sering dibutuhkan: memastikan sebuah input adalah angka murni sebelum operasi aritmatika. Ini mencegah error integer expression expected yang misterius saat $input ternyata berisi karakter acak:

Memastikan input adalah angka
read -p "Masukkan umur: " umur
if [[ "$umur" =~ ^[0-9]+$ ]]; then
    echo "Umur $umur tahun tercatat."
else
    echo "Error: '$umur' bukan angka murni."
fi
+ artinya satu atau lebih digit

Regex Ter-quote vs Tidak Ter-quote

Ada satu jebakan halus yang membedakan pemula dari yang berpengalaman: apa yang terjadi jika pola regex berada di sisi kanan dalam tanda kutip?

Quoted vs unquoted regex
# BUKAN regex — string literal yang persis
if [[ "$input" =~ "^[0-9]+$" ]]; then
    echo "String literal '^[0-9]+$' cocok"
fi
 
# Regex sungguhan — pola dievaluasi
if [[ "$input" =~ ^[0-9]+$ ]]; then
    echo "$input terdiri dari digit"
fi
Quoted membuat regex diperlakukan sebagai string literal

Aturannya sederhana: jika sisi kanan =~ berada dalam tanda kutip (atau diperoleh dari variabel yang ter-quote), itu diperlakukan sebagai string literal — karakter ^, [, +, $ dianggap sebagai karakter biasa, bukan meta-karakter regex. Inilah alasan mengapa banyak orang menulis:

Variabel regex di sisi kanan
pattern="^[0-9]+$"
if [[ "$input" =~ $pattern ]]; then
    echo "Cocok!"
fi
Variabel TANPA kutip akan dievaluasi sebagai regex

Variabel $pattern di atas tidak diberi tanda kutip, sehingga nilainya mengembang menjadi regex yang sesungguhnya dievaluasi. Jika kalian menulis =~ "$pattern", ia menjadi literal. Perilaku ini asimetris dengan sisi kiri — "$input" (sisi kiri) selalu boleh di-quote, sementara sisi kanan harus tanpa kutip jika ingin dievaluasi sebagai regex.

Kesalahan Umum dalam Case & Regex

Berikut peta jebakan yang paling sering dialami — simpan sebagai daftar periksa saat script kalian berperilaku aneh:

KesalahanGejalaSolusi
Lupa ;; di salah satu blokPerintah dari pola sebelumnya ikut tereksekusiPastikan setiap blok diakhiri ;;
Typo esac (misal esca)Error syntax error: unexpected end of fileCek penutup case — harus esac
case $VAR in tanpa kutipVariabel berisi glob/spasi cocok dengan pola tak terdugaTulis case "$VAR" in
Regex ter-quote di sisi kanan =~Pola diperlakukan literal, tidak pernah cocokTulis =~ ^pola$ tanpa kutip
Titik . tanpa escape di regex. cocok dengan karakter apa punTulis \. untuk titik literal
Menggunakan = bukan ==/=~Perbandingan selalu gagal untuk =~Pakai =~ hanya di [[ ]]

Note

=~ hanya tersedia di dalam [[ ]] (test majemuk bawaan Bash). Ia tidak berfungsi di [ ] (test POSIX) maupun di luar konteks kondisional. Jika kalian mendapatkan error seperti [[: not found atau =~: unary operator expected, itu tandanya kalian memakai bracket yang salah atau script dijalankan dengan sh alih-alih bash.

Penutup

Pada episode 10 ini kita telah menambah dua senjata penting ke gudang pengkondisian kalian. Dengan case ... esac kalian bisa mencocokkan satu variabel terhadap banyak pola dalam satu blok yang rapi — lengkap dengan pengelompokan pola (pattern1|pattern2), wildcard, dan *) sebagai cabang default — dan kita praktikkan langsung membangun command dispatcher yang menjadi tulang punggung banyak script service. Kemudian dengan =~ di dalam [[ ]] kalian bisa memvalidasi input seperti email, alamat IPv4, dan angka murni menggunakan regular expression, sembari memahami jebakan klasik: regex ter-quote di sisi kanan yang berubah menjadi string literal.

Prinsip yang harus kalian bawa pulang: if untuk keputusan biner, case untuk banyak pilihan, dan =~ untuk menguji bentuk string. Ketiganya adalah dasar dari pengambilan keputusan yang dinamis — dan pengambilan keputusan tanpa kemampuan mengulang seperti memasak nasi tanpa kompor: bisa, tapi belum lengkap.

Di episode 11 selanjutnya kita akan membuka babak baru: perulangan (loops). Kita mulai dengan for — dari bentuk sederhana for item in a b c, rentang {1..10}, hingga C-style for ((i=0; i<10; i++)), lengkap dengan break, continue, dan praktik mass rename file. Di sanalah kalian akan merasakan kekuatan penuh automation. Sampai jumpa di episode berikutnya!

Belajar BASH Scripting - Pengkondisian Lanjutan (Case Statement & Regex) | Belajar BASH Scripting