Mengotomatisasi pekerjaan berulang dengan perulangan `for`: dari bentuk daftar kata, rentang `{1..10}` dengan langkah `{1..10..2}`, hingga C-style `for ((i=0; i<10; i++))`. Lengkap dengan `break`, `continue`, iterasi file via glob, dan praktik mass rename beserta jebakan yang sering menjebak.

Setelah di episode 10 sebelumnya kita membahas pengkondisian lanjutan — case ... esac untuk mencocokkan banyak pola sekaligus dan operator =~ untuk validasi regex — pada episode kali ini kita membuka topik yang sama fundamentalnya: perulangan (loops). Kalau pengkondisian membuat script kalian bisa berpikir, perulangan membuat script kalian bisa bekerja.
Bayangkan kalian ditugaskan mengganti ekstensi dari 500 file .txt menjadi .md satu per satu. Secara manual, itu berjam-jam kerja yang membosankan dan rawan salah. Dengan perulangan, itu sepuluh baris script yang selesai dalam sepersekian detik. Inilah bedanya manusia dan mesin: mesin tidak bosan mengulang hal yang sama. Perulangan adalah cara kalian memerintahkan mesin untuk memanfaatkan keunggulan itu.
Pada episode kali ini kita fokus pada for — perulangan paling sering dipakai di Bash. Kita akan membedah tiga bentuknya: bentuk daftar kata, bentuk rentang angka, dan bentuk gaya-C. Kita tutup dengan break, continue, serta praktik nyata mass rename file. Episode berikutnya akan menyusul dengan while, until, dan membaca file baris demi baris.
Sebelum membedah sintaks, penting untuk memahami mengapa perulangan menjadi alasan utama orang menulis script di tempat pertama. Perulangan mengubah pekerjaan linier menjadi pekerjaan eksponensial: satu perintah yang kalian tulis sekali bisa berdampak ke puluhan, ratusan, bahkan ribuan item.
Coba bandingkan dua pendekatan ini. Untuk menghapus file backup basi:
rm backup-2024-01-01.tar.gz
rm backup-2024-01-02.tar.gz
rm backup-2024-01-03.tar.gz
for f in backup-*.tar.gz; do
rm "$f"
doneBaris diff di atas menangkap esensi automation: daripada menulis satu perintah per file, kalian menulis satu perintah untuk semua file. Ketika besok ada file ke-31, pendekatan pertama butuh kode baru; pendekatan kedua bekerja tanpa perubahan. Perulangan adalah cara berpikir data-driven: script tidak lagi peduli berapa banyak item, yang penting polanya.
for item in a b c (Daftar Kata)Bentuk paling dasar dari for adalah mengulang atas daftar kata yang dipisahkan spasi:
for kota in Jakarta Bandung Surabaya; do
echo "Kota: $kota"
doneBacaan baris ini: "untuk setiap kota di dalam daftar Jakarta Bandung Surabaya, lakukan blok do...done." Pada iterasi pertama kota berisi Jakarta, lalu Bandung, lalu Surabaya, dan perulangan selesai. Analogikan dengan sabuk konveyor pabrik: daftar kata adalah kotak-kotak yang lewat, dan blok do...done adalah stasiun kerja yang memproses satu kotak setiap kali.
Variabel kota adalah loop variable — ia hidup selama perulangan dan berubah setiap iterasi. Kalian bebas menamainya apa saja: file, i, user, server, dan sebagainya. Yang penting konsisten di dalam blok.
Daftar kata juga bisa berasal dari hasil ekspansi variabel:
server_list="web-01 web-02 db-01"
for server in $server_list; do
echo "Menyiapkan deploy ke $server"
done{1..10} dan {1..10..2}Menulis for i in 1 2 3 4 5 6 7 8 9 10 itu membosankan dan rawan typo. Bash menyediakan brace expansion untuk menghasilkan deret angka secara otomatis:
for i in {1..10}; do
echo "Iterasi ke-$i"
done
for genap in {0..10..2}; do
echo "Angka genap: $genap"
done{1..10} menghasilkan 1 2 3 4 5 6 7 8 9 10, dan {0..10..2} menghasilkan 0 2 4 6 8 10 — angka awal, angka akhir, dan langkah (step). Sintaks {start..end..step} adalah versi Bash modern; pastikan Bash kalian versi 4 atau lebih untuk dukungan penuh.
Brace expansion tidak hanya untuk angka. Kalian bisa menghasilkan deret huruf, atau kombinasi yang lebih kreatif:
for huruf in {a..f}; do
echo "Huruf: $huruf"
doneTip
Brace expansion terjadi sebelum loop dijalankan, bukan saat iterasi. Artinya {1..1000000} di sisi kanan akan membuat daftar satu juta kata di memori lebih dulu. Untuk perulangan sangat besar, lebih efisien memakai bentuk C-style (dibahas sebentar lagi) yang tidak membutuhkan daftar material di awal.
for ((i=0; i<10; i++))Bentuk ketiga meniru sintaks bahasa C dan sangat berguna saat kalian butuh kontrol numerik yang presisi — indeks, penghitung, atau deret dengan kondisi berhenti yang dinamis:
for ((i=0; i<10; i++)); do
echo "Hitungan ke-$i"
doneStruktur for (( ... )) memiliki tiga ruas yang dipisahkan ;:
| Ruas | Contoh | Fungsi |
|---|---|---|
| Inisialisasi | i=0 | Dijalankan sekali di awal, menyiapkan nilai awal |
| Kondisi | i<10 | Dievaluasi sebelum tiap iterasi; loop berhenti jika salah |
| Increment | i++ | Dijalankan setelah tiap iterasi, memperbarui penghitung |
Perhatikan bahwa variabel i di sini tidak memakai $ pada ruas inisialisasi dan increment — penulisan $i=0 justru salah. Ini salah satu kebiasaan yang perlu kalian pindah dari bahasa pemrograman lain: di dalam (( )), nama variabel ditulis tanpa $.
Bentuk C-style adalah pilihan tepat ketika jumlah iterasi dihitung — misalnya mencoba koneksi 5 kali sebelum menyerah:
for ((attempt=1; attempt<=5; attempt++)); do
echo "Percobaan ke-$attempt..."
if ping -c 1 server-db >/dev/null 2>&1; then
echo "Koneksi berhasil!"
break
fi
sleep 2
donebreak dan continue: Mengendalikan Alur PerulanganKadang kalian tidak ingin menjalankan semua iterasi. Dua kata kunci ini memberi kalian kendali seperti rem dan gas pada perulangan:
break — menghentikan perulangan seketika, melompat keluar dari do...done tanpa menunggu iterasi selesai.continue — melewati iterasi saat ini saja dan langsung melompat ke item berikutnya.Contoh penggunaan continue untuk melewati file yang bukan target, dan break untuk berhenti total:
for file in *.log; do
if [[ "$file" != app-* ]]; then
continue
fi
echo "Memproses $file"
done
for i in {1..10}; do
if (( i > 5 )); then
break
fi
echo "Masih di angka $i"
doneNote
Gunakan continue untuk filter dan break untuk early exit. Membaca kode yang penuh if...else di dalam loop akan cepat melelahkan; continue memindahkan logika pengecualian ke baris awal sehingga alur utama tetap mudah dibaca. Ingat pola ini: periksa, lalu continue, lalu kerjakan — sangat efektif di script panjang.
Salah satu penggunaan for yang paling sering di dunia nyata adalah mengiterasi file di sebuah direktori. Kuncinya: biarkan glob expansion yang menghasilkan daftar file, lalu bungkus variabel dengan tanda kutip saat menggunakannya:
for foto in *.jpg; do
echo "Memproses $foto"
# misal: resize, konversi, atau rename
doneBentuk for foto in *.jpg membuat Bash memperluas *.jpg menjadi daftar file yang cocok — dan yang penting, daftar ini sudah menjadi item terpisah, bukan satu string panjang. Ini berbeda dari for foto in $(ls *.jpg) yang rawan masalah kata yang mengandung spasi.
Perhatikan juga kasus ekstrem: jika tidak ada file .jpg, glob tanpa nullglob akan tetap menghasilkan string literal *.jpg dan loop menjalankan satu iterasi dengan $foto = *.jpg. Untuk mencegahnya, aktifkan opsi nullglob:
shopt -s nullglob
for foto in *.jpg; do
echo "Memproses $foto"
doneMari kita rangkai semuanya dalam satu studi kasus nyata yang sangat sering dibutuhkan admin: mass rename. Misalkan kalian memiliki sekumpulan file log dengan ekstensi lama .log yang harus diubah menjadi .txt:
#!/usr/bin/env bash
shopt -s nullglob
for file in *.log; do
baru="${file%.log}.txt"
mv "$file" "$baru"
echo "Renamed: $file -> $baru"
doneMari bedah baris kuncinya: "${file%.log}.txt" memakai parameter expansion untuk menghapus akhiran .log dari nama file (% memotong dari belakang), lalu menyisipkan .txt. Teknik mv "$file" "$baru" dengan kedua argumen ter-quote adalah golden rule untuk semua operasi file: selalu quote variabel yang berisi nama file, karena nama file di Linux boleh mengandung spasi, tanda kurung, bahkan newline.
Caution
Selalu uji script mass rename pada salinan data dulu. Satu typo di pola pemotongan nama bisa berujung pada puluhan file tertimpa atau hilang tanpa jejak. Kebiasaan yang sehat: jalankan versi dry-run (misal hanya echo "$file -> $baru") sebelum versi sungguhan yang berisi mv. Di produksi, mv juga bisa diperkuat dengan opsi -n (no-clobber) agar tidak menimpa file yang sudah ada.
for| Kesalahan | Gejala | Solusi |
|---|---|---|
for file in $list tanpa kutip pada penggunaan | File berspasi terpecah jadi dua item | Selalu "$file" saat memakai variabel |
for i in $(command) alih-alih array/glob | Output terpecah berdasarkan spasi & glob | Pakai array (mapfile) atau glob literal |
Lupa (( )) di C-style, menulis for (i=0; ...) | Error syntax error near unexpected token '(' | Tulis for (( ... )) dengan kurung ganda |
Ruas increment i++ memakai $i++ | Nilai tidak pernah bertambah benar / warning | Tulis i++ tanpa $ di dalam (( )) |
Glob tanpa nullglob saat tidak ada file | Loop berjalan sekali dengan literal *.jpg | shopt -s nullglob sebelum loop |
Satu kesalahan yang paling sering membuat pemula bingung adalah perulangan atas output command substitution:
for i in $(ls *.txt); do
echo "$i"
doneBaris $(ls *.txt) menjalankan ls, lalu hasilnya dipecah berdasarkan spasi (word splitting) dan karakter glob ikut diekspansi. Akibatnya: file bernama catatan penting.txt berubah menjadi dua iterasi (catatan dan penting.txt), dan file bernama dokumen[1].txt bisa berubah maknanya. Solusinya, seperti kita bahas, adalah glob literal (for i in *.txt) atau array:
for i in *.txt; do
echo "$i"
done
mapfile -t files < <(find . -name "*.txt")
for i in "${files[@]}"; do
echo "$i"
doneWarning
Kesalahan terburuk dari semuanya: infinite loop pada C-style for ((i=0; i<10; i++)) ketika ruas increment tertulis keliru, misalnya i-1 alih-alih i+1 — nilai i tidak pernah bertambah sehingga kondisi i<10 selalu benar. Selalu periksa bahwa ruas increment benar-benar menggerakkan penghitung mendekati kondisi berhenti. Sebagai jaring pengaman, jalankan script dengan timeout 5 ./script.sh saat sedang menulis loop baru.
Pada episode 11 ini kita telah menguasai perulangan for dalam tiga bentuknya: daftar kata (for item in a b c), rentang brace expansion ({1..10} dan {1..10..2}), dan gaya-C (for ((i=0; i<10; i++))). Kita juga mempelajari break untuk menghentikan perulangan seketika, continue untuk melewati satu iterasi, iterasi file via glob dengan nullglob, dan praktik mass rename yang menjadi salah satu penggunaan paling umum di dunia nyata. Yang tak kalah penting, kita mencatat jebakan klasik: unquoted variable, word splitting pada $(...), dan glob kosong.
Prinsip yang harus kalian bawa pulang: glorifikasi perulangan adalah keberanian menulis satu perintah untuk ribuan item. Dari mana pun daftar itemnya — daftar kata, rentang angka, hasil glob, atau output perintah — kuncinya selalu sama: biarkan Bash yang memecah daftar menjadi item, dan quote setiap variabel saat digunakan.
Namun for bukan satu-satunya perulangan di Bash. Ada situasi di mana kalian tidak tahu sebelumnya berapa kali harus mengulang — misalnya membaca file yang panjangnya tidak diketahui, atau menunggu suatu kondisi terpenuhi. Untuk itu, Bash menyediakan while dan until. Di episode 12 selanjutnya kita akan membahas perulangan bagian 2: while, until, dan membaca file baris demi baris — lengkap dengan pengolahan CSV dan jebakan subshell yang kerap membuat variabel "hilang" misterius. Sampai jumpa di episode berikutnya!