Belajar BASH Scripting - Quoting, Escaping & Word Splitting
Episode 4 of 27

Belajar BASH Scripting - Quoting, Escaping & Word Splitting

Membedah mekanisme quoting di BASH: kapan memakai kutip tunggal, kutip ganda, atau backslash, bagaimana word splitting bekerja dengan IFS, dan mengapa "$@" selalu lebih aman daripada $@ — dengan contoh nyata file bernama ber-spasi yang sering merusak skrip.

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

Pendahuluan

Setelah di episode 3 sebelumnya kita membedah variabel — deklarasi tanpa spasi, akses $VAR vs ${VAR}, export untuk mewariskan nilai, dan variabel bawaan seperti $PATH dan $HOME — pada episode kali ini kita membahas topik yang paling sering menjadi sumber bug halus di BASH: quoting, escaping & word splitting.

Inilah perbedaan antara pemula yang skripnya "kadang jalan, kadang tidak" dan praktisi yang skripnya selalu benar. Mengapa echo $nama bisa memecah nama menjadi dua kata? Mengapa rm $FILE bisa menghapus file yang salah ketika nama file mengandung spasi? Mengapa "$@" lebih aman daripada $@? Semua pertanyaan ini berakar pada satu mekanisme: cara BASH memecah string menjadi kata-kata — dan bagaimana kutip mengendalikannya.

Bayangkan BASH sebagai resepsionis yang sangat patuh. Setiap kali kalian memberikan sebuah string, dia memecahnya berdasarkan spasi menjadi "kata-kata" lalu meneruskan setiap kata sebagai argumen terpisah. Kadang itu yang kalian mau — kadang itu bencana. Kutip adalah cara kalian memberi instruksi tegas kepada resepsionis itu: "ini satu entitas utuh, jangan dipecah."

Pembahasan Utama

Tiga Jenis Quoting di BASH

BASH memberi tiga alat untuk mengendalikan interpretasi string:

AlatArtiContoh
Kutip tunggal '...'Literal penuh — tidak ada expansion sama sekali'$HOME tidak diekspansi'
Kutip ganda "..."Expansion variabel & command, tapi tidak word splitting & globbing"$HOME tidak dipecah"
Backslash \Meloloskan satu karakter berikutnya\$HOME = literal $HOME

Mari lihat perbedaannya secara nyata:

Kutip tunggal vs ganda vs backslash
echo '$HOME adalah literal'
echo "$HOME adalah expansion"
echo \$HOME hanya dolar-nya diloloskan
Output
$HOME adalah literal
/home/arman adalah expansion
$HOME hanya dolar-nya diloloskan

Aturan praktisnya:

  • Kutip tunggal ketika kalian ingin string apa adanya, persis — tidak ada $, tidak ada backtick, tidak ada apa pun yang diinterpretasi. Cocok untuk regex, string SQL, atau teks dengan banyak simbol.
  • Kutip ganda ketika kalian ingin expansion variabel ($VAR) dan command substitution ($(cmd)) terjadi, tetapi ingin hasilnya tetap satu kata — tidak dipecah oleh spasi.
  • Backslash untuk kasus satu karakter: \*, \$, \", atau spasi dalam nama file (My\ File).

Inside Double Quotes: Apa yang Tetap Bekerja?

Di dalam kutip ganda, tiga hal tetap terjadi — dan inilah yang membuatnya berbeda dari kutip tunggal:

Yang tetap jalan di dalam kutip ganda
file="laporan $(date +%Y).txt"   # command substitution jalan
dir="~/$USER/docs"               # variabel jalan, tapi "~" TIDAK diekspansi
star="a*b*"                      # glob TIDAK diperluas

Perhatikan dua kejutan kecil:

  1. Tanda ~ tidak diekspansi di dalam kutip. "~/$USER/docs" menghasilkan string literal ~/arman/docs — bukan /home/arman/arman/docs. Tilde hanya bekerja di posisi khusus (awal kata, tanpa kutip).
  2. Glob (*, ?) tidak diperluas di dalam kutip ganda. "*.log" tetap string *.log — tidak menjadi daftar file. Inilah mengapa globbing butuh "tanpa kutip".
EkspansiDalam '...'Dalam "..."Tanpa kutip
$VAR variabelTidakYaYa
$(cmd) substitutionTidakYaYa
Glob * ?TidakTidakYa
Word splittingTidakTidakYa
Tilde ~TidakTidakYa

Kutip ganda adalah "zona aman": variabel diekspansi, tapi hasilnya dilindungi dari pemecahan dan globbing. Inilah mengapa para praktisi hampir selalu menulis "$VAR" — bukan $VAR.

Note

Salah satu jebakan yang paling sering membuat pemula bingung: globbing. ls *.log di terminal menampilkan file log karena shell yang memperluas pola sebelum ls dijalankan. Tapi di dalam "*.log", pola tidak diperluas. Jika kalian butuh hasil glob disimpan sebagai daftar, gunakan array (dibahas di episode array) — bukan string yang di-quote.

Word Splitting & IFS

Sekarang kita sampai pada mekanisme yang menjadi akar sebagian besar bug: word splitting. Ketika BASH menemukan unquoted expansion ($VAR tanpa kutip), ia mengambil nilai variabel, lalu memecahnya berdasarkan karakter dalam IFS menjadi beberapa kata. Setiap kata menjadi argumen terpisah.

IFS (Internal Field Separator) adalah variabel bawaan yang menentukan karakter pemisah — default-nya: spasi, tab, dan newline.

Word splitting dalam praktik
nama="Arman Dwi"
printf '<%s>\n' $nama
printf '<%s>\n' "$nama"
Perbedaan jumlah argumen
<Arman>
<Dwi>
<Arman Dwi>

Lihat baris pertama: $nama tanpa kutip dipecah menjadi dua kata Arman dan Dwi, sehingga printf dipanggil dengan dua argumen dan mencetak dua baris. Dengan "$nama", seluruh nilai adalah satu argumen utuh — satu baris.

Inilah mengapa ekspansi tanpa kutip berbahaya: setiap spasi di dalam nilai variabel mengubah struktur argumen. Analoginya: kalian memberi resepsionis satu lembar berisi "Arman Dwi", tapi dia menganggap itu dua orang berbeda.

IFS bisa diubah — berhati-hatilah
data="satu,dua,tiga"
IFS=,
echo $data
unset IFS
echo "$data"
Output
satu dua tiga
satu,dua,tiga

Mengubah IFS adalah operasi yang memengaruhi seluruh skrip sesudahnya — itulah mengapa praktik yang aman adalah menyimpannya kembali (unset IFS atau IFS=$'\n' secara lokal di dalam fungsi). Untuk kebutuhan memecah string, mekanisme read -ra atau mapfile jauh lebih aman daripada mengubah IFS global.

Command Substitution: $(...) dan Backtick

Command substitution — yang sudah kalian lihat sejak episode 2 ($(date +%A)) — juga tunduk pada aturan quoting. Sintaks modern adalah $(perintah), dan sintaks lama (legacy) adalah backtick `perintah`. Kapan pun memungkinkan, gunakan $(...):

  • Lebih mudah dibaca, terutama saat bersarang.
  • Kutip dan backslash di dalamnya diperlakukan literal — di backtick, backslash berperilaku aneh.
  • Dapat bersarang dengan rapi: $(echo $(hostname)).
$(...) vs backtick
echo "Host: $(hostname)"          # modern, disarankan
echo "Host: `hostname`"           # legacy, hindari
echo "Tanggal: $(date +%Y-%m-%d)"

Perhatikan cara memakai hasilnya dengan aman:

Selalu kutip hasil command substitution
path="$(pwd)/file.txt"     # aman: hasil pwd digabung tanpa splitting
list="$(ls)"               # hasil ls yang berisi spasi tetap satu string
echo "$path"

Sama seperti variabel, hasil command substitution yang tidak di-quote juga akan mengalami word splitting. Aturan yang sama berlaku: jika ingin hasilnya utuh, bungkus dengan kutip ganda — "$(perintah)".

Warning

Aturan emas yang akan menyelamatkan kalian dari puluhan bug: selalu kutip ganda ekspansi variabel — kecuali kalian benar-benar ingin word splitting. "$VAR" untuk nilai yang ingin dianggap utuh; $VAR hanya saat kalian sengaja ingin memecahnya (jarang terjadi, dan itu pun ada cara yang lebih aman). BASH tidak punya "kutip pintar" — ia menuruti apa yang kalian tulis.

Mengapa "$@" Selalu Lebih Aman daripada $@

Setiap skrip BASH menerima argumen dalam variabel khusus: $1, $2, $3, dan seterusnya, plus $@ dan $* yang mewakili semua argumen. Perbedaan keduanya dalam hal word splitting sangat menentukan:

  • $@ — semua argumen, dipecah lagi oleh word splitting (karena unquoted).
  • "$@" — semua argumen, masing-masing tetap utuh sebagai satu entitas terpisah. Inilah perilaku yang selalu kalian inginkan.
  • $* / "$*" — semua argumen digabung menjadi satu string dipisah spasi.
Pentingnya
#!/bin/bash
 
for file in $@; do
  echo "Mencoba hapus: $file"
  rm "$file"
done
Buat file uji lalu panggil skrip
touch "Laporan Akhir.txt"
./hapus.sh Laporan Akhir.txt
Masalah:
Mencoba hapus: Laporan
Mencoba hapus: Akhir

Skrip di atas mencoba menghapus satu file bernama Laporan Akhir.txt, tapi karena $@ tidak di-quote, BASH memecahnya menjadi dua argumen: Laporan dan Akhir.txt. Tidak ada file yang berhasil dihapus — dan di dunia nyata, bug seperti ini bisa menghapus file yang salah. Sekarang perbaiki:

Perbaikan: gunakan
#!/bin/bash
 
for file in "$@"; do
  echo "Mencoba hapus: $file"
  rm "$file"
done
Uji ulang
./hapus.sh "Laporan Akhir.txt"
ls Laporan\ Akhir.txt 2>&1 || echo "File berhasil dihapus"
Output
Mencoba hapus: Laporan Akhir.txt
File berhasil dihapus

Sekarang "Laporan Akhir.txt" diperlakukan sebagai satu argumen utuh — persis seperti yang diinginkan. Bedanya "$@" vs $@ adalah beda antara menghapus file yang benar dan menghapus file yang salah. Ini bukan teori: file bernama ber-spasi adalah kenyataan sehari-hari di server produksi.

Tip

Jadikan "$@" sebagai kebiasaan otomatis di setiap skrip yang mem-forward argumen. Pola pasangannya yang juga wajib: "${1:-}" untuk $1 yang bisa kosong, dan "$file" untuk nama file dari variabel. Kutip ganda adalah "sabuk pengaman" BASH — kenakan setiap saat, bukan hanya saat takut.

Kesalahan Umum (Common Pitfalls)

  1. echo $VAR tanpa kutip. Nilai mengandung spasi → dipecah → output tidak rapi, atau argumen bergeser. Selalu echo "$VAR".

  2. rm $FILE dengan nama ber-spasi. rm menerima dua argumen File dan Akhir.txt alih-alih satu File Akhir.txt. Selalu rm "$FILE".

  3. Mengandalkan ~ di dalam kutip. "~/backup" tidak menjadi home — gunakan "$HOME/backup" atau ~/backup tanpa kutip.

  4. Mencampur "$@" dengan $*. $* menggabungkan semua argumen menjadi satu string — hampir tidak pernah yang kalian inginkan saat mem-forward argumen. Gunakan "$@".

  5. Mengubah IFS tanpa menyimpan kembali. IFS global yang berubah membuat seluruh skrip sesudahnya memecah string secara aneh. Simpan lama, ubah, lalu kembalikan: old_ifs=$IFS; IFS=,; ...; IFS=$old_ifs.

  6. Variabel kosong tanpa kutip. rm $file dengan file="" menjadi rm (tanpa argumen) — atau lebih buruk, rm dengan argumen yang tidak diinginkan. "$file" tetap satu argumen kosong yang aman.

KesalahanGejalaSolusi
echo $namaSpasi dalam nilai terpecah jadi beberapa baris/kataecho "$nama"
rm $fileFile bernama ber-spasi tidak terhapus / file salah terhapusrm "$file"
for f in $@Argumen ber-spasi dipecah jadi banyakfor f in "$@"
"~/backup"Path literal ~/backup, bukan /home/user/backup"$HOME/backup"
IFS diubah tanpa dikembalikanWord splitting aneh di seluruh skripSimpan & kembalikan IFS

Ada satu pola yang paling sering melengkapi pembahasan quoting: menangani file bernama aneh. Ketika kalian benar-benar harus memproses nama file dengan spasi, karakter aneh, bahkan newline, gunakan null-separated (-print0 + while read -d '') — pola yang aman terhadap semua karakter. Ini akan dibahas mendalam di episode tentang iterasi file, tapi catat istilahnya sejak sekarang.

Caution

BASH tidak memecah string secara otomatis di semua konteks. Word splitting hanya terjadi pada ekspansi yang tidak di-quote — bukan pada string literal. echo a b tetap mencetak a b (dua argumen literal), sedangkan x="a b"; echo $x dipecah. Memahami di mana splitting terjadi dan di mana tidak adalah kunci membaca skrip yang rumit tanpa salah paham.

Penutup

Pada episode 4 ini kalian telah memahami mekanisme yang menentukan keandalan setiap skrip: tiga jenis quoting ('...' literal, "..." yang mengekspansi variabel tapi melindungi dari splitting, dan \ untuk satu karakter), word splitting dengan IFS sebagai pemecah default, mengapa "$@" adalah bentuk paling aman untuk meneruskan argumen, dan pola ${VAR:-default} yang kembali dipakai untuk melindungi variabel kosong.

Inti yang harus kalian bawa:

  • Kutip tunggal = literal penuh; kutip ganda = ekspansi tanpa pemecahan; backslash = loloskan satu karakter.
  • Word splitting memecah unquoted expansion berdasarkan IFS (spasi/tab/newline) — ini sumber sebagian besar bug.
  • Selalu kutip ganda variabel kecuali kalian sengaja ingin splitting.
  • "$@" mempertahankan setiap argumen utuh; $@ tanpa kutip merusak argumen ber-spasi.
  • ~ tidak diekspansi di dalam kutip; glob tidak diperluas di dalam kutip ganda.

Quoting adalah "tata krama" BASH — kecil, mudah diabaikan, dan menentukan apakah skrip kalian sopan (benar) atau kasar (rusak di saat genting). Di episode 5 selanjutnya kita akan memadukan semuanya dengan topik yang membuat BASH terasa ajaib: globbing & pathname expansion — pola *, ?, dan [] untuk mencocokkan banyak file, perbedaan glob di dalam dan di luar kutip yang sudah kalian rasakan hari ini, hingga globstar untuk pencarian bertingkat. Fondasi quoting yang kalian bangun sekarang akan membuat episode itu terasa seperti bermain, bukan bertarung dengan shell. Sampai jumpa!

Belajar BASH Scripting - Quoting, Escaping & Word Splitting | Belajar BASH Scripting