Menguasai cara bash memperluas pola menjadi daftar file nyata: wildcard *, ?, [...], extglob, brace expansion {1..10}, globstar **, opsi shopt (nullglob, failglob, dotglob, nocaseglob), serta kebiasaan penting memakai glob dan tidak pernah mem-parse output ls di dalam skrip.

Setelah di episode 6 sebelumnya kita membahas input interaktif dengan read dan menu dengan select — bagaimana skrip menerima data dari manusia — pada episode kali ini kita beralih ke sumber data yang jauh lebih sering dipakai di otomasi: file-file di dalam filesystem. Kita akan mempelajari bagaimana bash memperluas pola seperti *.log menjadi daftar file yang cocok, sebuah mekanisme yang disebut globbing (atau pathname expansion).
Kalau kalian sudah pernah mengetik rm *.tmp atau ls report_*.pdf, kalian sebenarnya sudah memakai globbing tanpa sadar. Tapi di balik kesederhanaannya tersimpan banyak jebakan: glob tidak berekspansi di dalam tanda kutip, pola yang tidak cocok bisa "menembus" sebagai string literal, dan kebiasaan ls *.txt di terminal bisa berubah menjadi bencana saat ditulis ke dalam skrip. Episode ini akan membuat kalian memahami globbing secara fundamental — bukan sekadar "menghafal sintaks" — sehingga skrip kalian bisa memproses file dengan aman.
*, ?, dan [...]Globbing dimulai dengan tiga wildcard dasar. Mari buat direktori latihan dengan beberapa file:
mkdir -p /tmp/latihan-glob && cd /tmp/latihan-glob
touch report_2026.pdf report_2026.txt laporan_2025.pdf
touch foto1.jpg foto2.jpg foto10.jpg catatan.md* — Nol atau Lebih KarakterWildcard * cocok dengan nol atau lebih karakter apa pun (kecuali karakter yang diawali titik di awal nama file, dan slash). Ia adalah wildcard paling sering dipakai:
echo *.pdf
echo report_*
echo *2026*report_2026.pdf laporan_2025.pdf
report_2026.pdf report_2026.txt
report_2026.pdf report_2026.txtPerhatikan hal penting: report_* cocok dengan report_2026.pdf dan report_2026.txt — * bisa mewakili banyak karakter berbeda. Dan *2026* cocok dengan apa pun yang mengandung "2026" di tengahnya. Inilah mengapa globbing begitu berharga: kita tidak perlu tahu nama file persisnya, cukup polanya.
? — Tepat Satu KarakterBerbeda dengan *, ? cocok dengan tepat satu karakter:
echo foto?.jpg
echo foto1?.jpgfoto1.jpg foto2.jpg
foto10.jpgPerhatikan foto?.jpg hanya mencocokkan foto1.jpg dan foto2.jpg — bukan foto10.jpg, karena ? mewakili tepat satu karakter, dan foto10.jpg punya dua karakter antara foto dan .jpg. Sebaliknya, foto1?.jpg mencocokkan foto10.jpg — satu karakter di posisi ? itu. Ini presisi yang berguna saat kalian ingin menangkap hanya file dengan penanda satu digit, misalnya bab_?.md untuk bab 1–9 tetapi bukan bab 10.
[...] — Set KarakterKurung siku mencocokkan satu karakter dari set yang disebutkan. Ini seperti ? tapi dengan pembatasan:
echo foto[0-9].jpg
echo *[0-9]*.pdffoto1.jpg foto2.jpg
report_2026.pdffoto[0-9].jpg mencocokkan foto1.jpg, foto2.jpg — tapi bukan foto10.jpg (lagi-lagi: satu karakter), dan [0-9] membatasi karakter itu hanya digit. Kita juga bisa menulis set eksplisit: [abc], rentang [a-z], atau negasi [!0-9] (bukan digit). Kekuatan [...] adalah presisi: kalian bisa memilih hanya file dengan digit, hanya huruf kecil, dan seterusnya.
Untuk pola yang lebih kompleks, bash menyediakan extended globbing (extglob) yang harus diaktifkan:
shopt -s extglob
shopt extglobextglob onExtglob memperkenalkan lima operator yang bekerja seperti "regex mini":
| Pola | Arti | Contoh |
|---|---|---|
?(pola) | Nol atau satu kali | foto?(1).jpg → foto.jpg, foto1.jpg |
*(pola) | Nol atau lebih | foto*.jpg sederhananya bisa ditulis *(foto)1.jpg |
+(pola) | Satu atau lebih | +([0-9]) → satu atau lebih digit |
@(a|b) | Tepat salah satu (alternatif) | foto.@(jpg|png) → jpg atau png |
!(pola) | Negasi — apa pun kecuali pola | !(report_*) → semua file selain report_* |
Contoh paling berguna dalam praktik DevOps — memilih banyak ekstensi sekaligus:
echo *.@(jpg|png|md)foto1.jpg foto2.jpg foto10.jpg catatan.mdTanpa extglob, cara konvensionalnya adalah tiga pola terpisah (*.jpg *.png *.md) atau pola yang lebih samar. Dengan @(jpg|png|md), kalian mengekspresikan niat dengan jelas: "file dengan salah satu ekstensi ini".
Note
Extglob sudah aktif secara default di banyak distro dan di dalam skrip bash interaktif, tapi jangan mengandalkannya — beberapa lingkungan (misalnya sh yang menganggap pola extglob sebagai error sintaks) menonaktifkannya. Jika skrip kalian memakai pola extglob, tambahkan shopt -s extglob di dekat set -euo pipefail agar perilakunya deterministik, apa pun lingkungannya.
Pola {1..10}, {a..z}, dan {jpg,png} sering disalahpahami sebagai bagian dari globbing. Sebenarnya brace expansion adalah mekanisme terpisah yang terjadi sebelum globbing — dan ini perbedaan yang sangat penting.
Globbing mencocokkan pola dengan file yang benar-benar ada di filesystem — jika tidak ada yang cocok, hasilnya bisa berupa string literal yang tidak berguna. Brace expansion, sebaliknya, adalah generator teks murni: ia menghasilkan daftar string berdasarkan aturan matematis, tanpa peduli apakah file-nya ada atau tidak. Lihat contoh berikut — tidak ada file apa pun yang dibuat, tapi outputnya tetap keluar:
echo {1..5}
echo {a..c}
echo {jpg,png,svg}1 2 3 4 5
a b c
jpg png svgBrace expansion memproduksi string-string itu meskipun file-nya tidak ada — ia tidak pernah melihat filesystem. Inilah alasan kenapa for i in {1..10} bisa menghasilkan 1 sampai 10 tanpa perlu ada file bernama 1, 2, dst. Ini adalah alat pembangkit daftar (list generator), bukan alat pencocok file. Mengingat perbedaan ini akan menyelamatkan kalian dari kebingungan saat menggabungkannya dengan glob:
echo report_{2025,2026}.pdf
echo {1..3}.jpgreport_2025.pdf report_2026.pdf
1.jpg 2.jpg 3.jpgUrutan kejadiannya: bash pertama memperluas {2025,2026} menjadi report_2025.pdf report_2026.pdf, lalu menyerahkan hasilnya ke globbing untuk mencocokkan file yang benar-benar ada. Analoginya: brace expansion adalah mesin pencetak daftar belanja, sedangkan globbing adalah kuli pasar yang mengambil barang-barang yang benar-benar tersedia di rak.
** — Rekursif ke SubdirektoriSecara default, * tidak menembus direktori. Pola ** — jika opsi globstar aktif — mencocokkan file di semua tingkat kedalaman subdirektori:
shopt -s globstar
echo **/*.logapp.log
api/server.log
web/prod/deploy.logTanpa globstar, **/*.log berperilaku sama dengan */*.log — hanya satu tingkat. Dengan globstar, ia menjelajah ke kedalaman tak terbatas. Ini sangat berguna untuk skrip pembersihan (menghapus semua *.tmp di dalam tree) atau pencarian file log. Catatan: shopt -s globstar tidak diaktifkan di semua sistem, jadi tambahkan di skrip yang membutuhkannya.
shopt dan Teman-TemannyaInilah area paling berbahaya dari globbing. Apa yang terjadi jika *.log tidak cocok dengan file apa pun? Jawabannya mengejutkan: pola itu diteruskan sebagai string literal — *.log tetap *.log! Coba di direktori latihan kita (yang tidak punya .log):
echo *.log*.logBayangkan bahayanya dalam skrip: rm *.log saat tidak ada file .log akan memanggil rm dengan argumen literal *.log — dan jika ada file bernama *.log di direktori itu, file tersebut ikut terhapus. Untungnya bash menyediakan tiga opsi shopt untuk mengendalikan perilaku ini:
| Opsi | Perilaku saat pola tidak cocok | Kapan Dipakai |
|---|---|---|
nullglob | Pola menjadi kosong (tidak ada argumen) | Skrip produksi — pola tanpa match jadi "tidak ada file", aman |
failglob | Error dan skrip berhenti | Saat match kosong adalah kondisi yang harus dianggap kegagalan |
dotglob | * ikut mencocokkan file tersembunyi (. di depan) | Saat perlu memproses .env, .git, dsb. |
nocaseglob | Pencocokan tidak peka huruf besar/kecil | Saat ekstensi bisa LOG, Log, log |
Pola terbaik untuk skrip produksi adalah mengaktifkan nullglob:
shopt -s nullglob
for f in *.log; do
echo "Memproses: $f"
done
echo "Selesai — loop tidak dieksekusi sama sekali jika tidak ada .log"Selesai — loop tidak dieksekusi sama sekali jika tidak ada .logWarning
Perhatikan bahwa nullglob dan failglob tidak bisa aktif bersamaan. Pilih sesuai semantik: nullglob jika "tidak ada file" adalah hasil yang wajar (misalnya pembersihan log), failglob jika tidak adanya match harus menghentikan skrip (misalnya memastikan ada file backup sebelum melanjutkan). Keputusan ini harus eksplisit, bukan kebetulan.
Contoh kasus nyata yang akan kalian temui hampir setiap minggu: merename banyak file sekaligus berdasarkan pola. Misalkan kita punya foto bernama IMG_1001.JPG, IMG_1002.JPG, dst., dan ingin menambahkan awalan tanggal:
#!/usr/bin/env bash
shopt -s nullglob nocaseglob
for f in IMG_*.JPG; do
mv "$f" "2026-$(basename "$f")"
echo " $f → 2026-$(basename "$f")"
done IMG_1001.JPG → 2026-IMG_1001.JPG
IMG_1002.JPG → 2026-IMG_1002.JPG
IMG_1003.JPG → 2026-IMG_1003.JPGKenapa kita pakai nocaseglob? Karena file bisa bernama .JPG, .jpg, atau .Jpg. Tanpa nocaseglob, kita harus menulis dua pola atau kehilangan sebagian file. Dan nullglob memastikan: jika tidak ada foto, loop tidak berjalan (alih-alih mencoba mv file bernama IMG_*.JPG yang tidak ada — yang akan error membingungkan).
lsBanyak tutorial lama menyarankan menulis loop seperti ini:
for f in $(ls *.jpg); do
mv "$f" "backup_$f"
doneMengapa mem-parse ls adalah kesalahan besar? Ada empat alasan fundamental:
$(ls *.jpg) menghasilkan satu string yang kemudian dipecah berdasarkan spasi. File liburan pantai.jpg akan menjadi tiga "file": liburan, pantai.jpg — dan mv akan error.; atau $(...) di dalam nama file bisa dieksekusi sebagai perintah — celah keamanan serius.ls mengubah tampilan. Beberapa implementasi ls mengubah output (warna, kolom, urutan) tergantung terminal — bukan kontrak yang stabil untuk diparsing.for f in *.jpg memberi kalian daftar file yang tepat, dengan penanganan spasi yang benar, tanpa proses eksternal.Aturan praktis yang wajib kalian hafal: gunakan glob untuk mencantumkan file di dalam skrip; gunakan ls hanya untuk dilihat manusia di terminal. Untuk daftar file yang butuh metadata (ukuran, waktu modifikasi), gunakan find — bukan ls — karena find juga menghasilkan daftar yang aman untuk diproses.
echo "*.jpg" # literal, bukan daftar file!Ini adalah salah satu alasan mengapa kutip ganda di mv "$f" ... tidak masalah (kita ingin nilai variabel, bukan pola) — tapi ketika kalian menulis rm "*.tmp", bash akan meneruskan literal *.tmp. Aturan: quote variabel, jangan quote pola glob.
Sudah dibahas — ini penyebab utama rm menghapus file yang salah. Aktifkan nullglob di skrip produksi.
shopt -s extglob / globstarPola seperti @(jpg|png) atau **/*.log yang tidak aktif opsi-nya akan gagal atau berperilaku tidak sesuai harapan. Set eksplisit di awal skrip.
ls untuk mendapatkan daftar fileKebiasaan paling berbahaya dan paling sering ditiru dari tutorial lama. Gunakan glob, atau find untuk kasus yang butuh metadata.
Menggabungkan keduanya tanpa sadar menghasilkan kejutan. echo {1..3}.jpg menghasilkan string 1.jpg 2.jpg 3.jpg walau file-nya tidak ada. Brace expansion adalah generator teks; globbing adalah pencocok file. Urutannya: brace dulu, glob kemudian.
Pada episode 7 ini, kita telah membedah globbing secara menyeluruh: wildcard dasar *, ?, [...]; extglob @(), *(), +(), ?(), !(); perbedaan mendasar brace expansion {1..10} yang merupakan generator teks murni (bukan globbing, dan terjadi lebih dulu); globstar ** untuk pencarian rekursif; serta opsi shopt — nullglob, failglob, dotglob, nocaseglob — yang mengendalikan apa yang terjadi saat pola tidak cocok.
Inti yang harus dibawa pulang:
* = nol atau lebih karakter; ? = tepat satu; [...] = satu karakter dari set.nullglob (atau failglob) untuk mengendalikan pola tanpa match — jangan biarkan literal lolos.ls — gunakan glob atau find.Dengan kemampuan membaca daftar file dari filesystem, skrip kalian sudah bisa menangani banyak tugas operasional. Tapi prosesor untuk mengolahnya masih belum lengkap: bagaimana kalau ingin menghitung, membandingkan, atau menghitung persentase disk yang terpakai? Di episode 8 selanjutnya, kita akan membahas Operasi Aritmatika & Evaluasi Matematika — keterbatasan integer bash, arithmetic expansion $(( )), operator + - * / % **, penggunaan bc untuk bilangan pecahan, hingga skrip kalkulator pemakaian disk. Sampai jumpa!