Variabel menyimpan nilai berupa teks agar dapat dipakai ulang di banyak tempat, sedangkan quoting mengatur bagaimana nilai itu diperluas menjadi bagian perintah. Topik ini membahas penugasan variabel, cara bash melakukan ekspansi, serta aturan quoting yang mencegah dua kegagalan paling umum pada skrip shell, yaitu pecah kata yang tidak disengaja dan pengembangan glob di tempat yang tidak dimaksud. Nilai yang memuat spasi dapat ditangani dengan aman baik di terminal maupun di dalam skrip.
Penugasan dan ekspansi#
Penugasan variabel ditulis tanpa spasi di sekitar tanda sama dengan, yaitu KOTA="Bandung", bukan KOTA = "Bandung". Aturan ini bukan sekadar soal gaya, karena spasi mengubah makna baris tersebut. Jalankan perintah berikut untuk melihat apa yang terjadi apabila aturan itu dilanggar.
# kesalahan: spasi di sekitar tanda sama dengan
KOTA = "Bandung"bash: line 3: KOTA: command not foundBash membaca baris itu sebagai perintah bernama KOTA dengan dua argumen, yaitu = dan Bandung, lalu melaporkan bahwa perintah tersebut tidak ada. Pesannya muncul dan tidak ada variabel yang terbentuk. Penugasan yang benar selalu menempelkan tanda sama dengan pada nama variabel dan nilainya.
Nilai variabel dipanggil dengan menuliskan tanda dolar di depan namanya. Bentuk panjang dengan kurung kurawal, yaitu ${KOTA}, diperlukan saat nilai disambung dengan teks lain yang diawali karakter yang sah sebagai nama variabel. Perhatikan demonstrasi berikut yang membandingkan kedua bentuk tersebut.
# kurung kurawal memisahkan nama variabel dari teks di sekitarnya
HARGA=85
HARGA00="variabel lain"
printf '%s\n' "$HARGA00"
printf '%s\n' "${HARGA}00"variabel lain
8500Tanpa kurung kurawal, $HARGA00 dibaca sebagai variabel bernama HARGA00, dan itulah yang dibaca bash pada baris pertama. Dengan kurung kurawal, bash mengetahui bahwa nama variabelnya berhenti pada HARGA dan teks 00 adalah sambungannya, sehingga keluarannya menjadi 8500. Menggunakan bentuk kurung kurawal secara konsisten mencegah kekeliruan semacam ini.
Pecah kata pada ekspansi tanpa quoting#
Ekspansi variabel tanpa quoting dipecah menjadi beberapa bagian pada setiap spasi, tab, atau baris baru dalam nilainya, suatu perilaku yang disebut pecah kata (word splitting). Pemisahan ini diatur oleh variabel khusus bernama IFS, yang secara bawaan berisi karakter-karakter tersebut. Perilakunya dapat diamati dengan variabel berisi tiga butir belanja.
# ekspansi tanpa quoting dipecah pada setiap spasi
DAFTAR="susu telur beras"
printf '%s\n' $DAFTARsusu
telur
berasSatu variabel menghasilkan tiga baris karena printf menerima tiga argumen hasil pemecahan. Pada banyak situasi, pecah kata memang diinginkan, misalnya saat mengulangi daftar butir. Masalah muncul ketika pemecahan tidak diperhitungkan, seperti nilai yang berisi nama berkas dengan spasi.
Quoting dengan tanda kutip ganda membuat seluruh hasil ekspansi diperlakukan sebagai satu bagian, berapa pun spasinya. Demonstrasi berikut menggunakan variabel yang sama dengan tambahan tanda kutip ganda.
# kutip ganda menjaga nilai tetap satu bagian
printf '%s\n' "$DAFTAR"susu telur berasKeluaran kini berupa satu baris berisi nilai utuh. Perbedaan satu baris dan tiga baris inilah yang membedakan perintah yang benar dan perintah yang menghapus atau memproses berkas yang keliru.
Tanda kutip tunggal dan ganda#
Tanda kutip ganda memperluas variabel, sedangkan tanda kutip tunggal mempertahankan seluruh isi apa adanya. Dua bentuk berikut menggunakan teks yang sama, tetapi menghasilkan keluaran yang berbeda karena perlakuan keduanya terhadap tanda dolar berbeda.
# kutip ganda memperluas variabel, kutip tunggal mempertahankan apa adanya
BUTIR="susu formula"
printf '%s\n' "butir: $BUTIR"
printf '%s\n' 'butir: $BUTIR'butir: susu formula
butir: $BUTIRPada tanda kutip tunggal, $BUTIR dicetak persis seperti yang ditulis karena tidak ada ekspansi yang berlangsung. Di dalam tanda kutip tunggal, tidak ada satu karakter pun yang memicu ekspansi, sehingga satu-satunya cara menulis tanda kutip tunggal di dalamnya adalah menutup kutipan, menulis \', lalu membukanya kembali. Bentuk ini berguna untuk teks tetap yang memuat tanda dolar atau karakter khusus lainnya.
Di dalam tanda kutip ganda, tanda dolar, tanda kutip terbalik, garis miring terbalik, dan tanda kutip ganda sendiri tetap memiliki makna khusus, sedangkan karakter lain diperlakukan apa adanya. Untuk nilai variabel, tanda kutip ganda adalah pilihan yang tepat, dan seluruh contoh pada topik ini mengikutinya. Ketika ragu, kutip ganda pada setiap ekspansi adalah kebiasaan yang aman, karena pecah kata dan pengembangan glob tidak pernah terjadi di dalamnya.
Bencana ekspansi tanpa quoting#
Dampak pecah kata paling mudah diamati pada perintah yang membuat atau mengubah berkas. Variabel berikut berisi nama direktori dengan satu spasi di tengahnya. Jalankan demonstrasi ini untuk melihat hasil ekspansi tanpa quoting, kemudian periksa isi direktori kerjanya.
# siapkan direktori kerja untuk demonstrasi
mkdir -p demo-quoting
cd demo-quoting
LOKASI="arsip cadangan"
mkdir $LOKASI
ls -1
cd ..arsip
cadanganSatu perintah mkdir menghasilkan dua direktori, yaitu arsip dan cadangan, karena nilainya dipecah pada spasi sebelum perintah dijalankan. Perintah mkdir menerima dua argumen dan memperlakukannya sebagai dua nama direktori. Tidak ada peringatan apa pun yang muncul, sehingga kesalahan semacam ini sering baru disadari setelah akibatnya terbentuk.
Penambahan tanda kutip ganda membuat nilai itu diperlakukan sebagai satu nama direktori utuh.
# dengan quoting, nilai diperlakukan sebagai satu nama direktori
LOKASI="arsip cadangan"
cd demo-quoting
mkdir "$LOKASI"
ls -1
cd ..arsip
arsip cadangan
cadanganDirektori bernama arsip cadangan kini muncul sebagai satu entri di antara kedua direktori hasil sebelumnya. Pola yang sama berlaku untuk setiap perintah yang menerima nama berkas, termasuk cp, mv, dan rm.
Glob yang tidak disengaja#
Glob adalah pola nama berkas yang memuat karakter pengganti seperti *, yang oleh bash dikembangkan menjadi daftar nama berkas yang cocok. Ekspansi variabel tanpa quoting juga mengalami pengembangan ini, sehingga nilai yang tampak jinak dapat berubah menjadi daftar berkas. Siapkan dahulu beberapa berkas contoh, kemudian amati dua perlakuan terhadap pola yang sama.
# siapkan beberapa berkas teks untuk demonstrasi glob
printf 'isi a\n' > nota-a.txt
printf 'isi b\n' > nota-b.txt
printf 'isi c\n' > nota.txt# pola yang di-quote tetap harfiah, tanpa quote mengembang menjadi nama berkas
POLA="*.txt"
printf '%s\n' "$POLA"
printf '%s\n' $POLA*.txt
nota-a.txt
nota-b.txt
nota.txtBaris pertama mencetak pola apa adanya karena tanda kutip ganda menonaktifkan pengembangan glob. Baris kedua melalui dua tahap, yaitu ekspansi variabel yang menghasilkan teks *.txt, kemudian pengembangan glob yang mengubah teks itu menjadi tiga nama berkas yang cocok. printf menerima tiga argumen dan mencetaknya satu per satu.
Perilaku ini kadang digunakan dengan sengaja, tetapi lebih sering menjadi sumber kejutan karena daftar yang dihasilkan bergantung pada isi direktori saat eksekusi. Skrip yang berjalan di direktori berbeda akan menghasilkan daftar berbeda. Menuliskan variabel di dalam tanda kutip ganda menonaktifkan kedua tahap tersebut sekaligus dan menjadikan perilaku skrip mudah diprediksi.
Quoting pada skrip#
Skrip berikut memperhitungkan total belanja dari tiga variabel yang sudah ditetapkan. Seluruh variabel ditulis di dalam tanda kutip ganda, termasuk NAMA_BARANG yang memuat spasi, sehingga pecah kata dan pengembangan glob tidak pernah terjadi. Penjumlahan dilakukan oleh bash melalui ekspansi aritmetika $(( )).
#!/usr/bin/env bash
# belanja.sh: menghitung total belanja dari variabel yang sudah ditetapkan
set -euo pipefail
NAMA_BARANG="susu formula"
HARGA_SATUAN=8500
JUMLAH=2
TOTAL=$((HARGA_SATUAN * JUMLAH))
printf 'Barang : %s\n' "$NAMA_BARANG"
printf 'Jumlah : %d buah\n' "$JUMLAH"
printf 'Total : Rp%d\n' "$TOTAL"# jalankan skrip penghitung belanja
bash belanja.shBarang : susu formula
Jumlah : 2 buah
Total : Rp17000Baris set -euo pipefail mengikuti kebiasaan yang sudah diterapkan pada topik skrip pertama, yaitu menghentikan skrip saat terjadi kesalahan. Nilai susu formula lolos utuh dari ekspansi karena selalu ditulis dengan quoting. Hasil perhitungan 17000 berasal dari ekspansi aritmetika, yang tidak memerlukan quoting karena beroperasi pada angka, bukan teks.
Disiplin yang sama berlaku persis di dalam skrip dan di terminal, karena keduanya menggunakan mekanisme ekspansi yang sama. Menyimpan nilai pada variabel di bagian atas skrip membuat perubahan harga cukup dilakukan di satu tempat, dan setiap pemakaian variabel yang diberi tanda kutip ganda memastikan perubahan itu tidak membawa efek samping baru. Kebiasaan ini menjadi semakin penting saat skrip berkembang, karena nilai yang semula berupa satu kata bisa berubah menjadi kalimat panjang pada pembaruan berikutnya.
Kesalahan umum#
Spasi di sekitar tanda sama dengan. Penugasan KOTA = "Bandung" dijalankan sebagai perintah bernama KOTA dan berakhir dengan pesan command not found, sebagaimana diperlihatkan pada bagian penugasan. Pastikan tanda sama dengan menempel pada nama variabel dan nilainya.
Variabel berisi spasi ditulis tanpa quoting. Nama berkas seperti laporan bulanan.txt yang disimpan dalam variabel lalu diekspansi tanpa kutip ganda dipecah menjadi dua bagian. Perintah yang menerimanya bekerja pada dua target yang salah, dan dampaknya paling serius pada perintah yang menghapus berkas. Perbaikannya adalah menuliskan $BERKAS sebagai "$BERKAS" pada setiap pemakaiannya, bukan hanya pada tempat yang tampak berbahaya, karena penerapan yang hanya sebagian mudah terlewat pada suntingan berikutnya.
Kutip tunggal digunakan ketika ekspansi diharapkan. Perintah printf 'harga: $HARGA\n' mencetak teks $HARGA apa adanya karena tanda kutip tunggal menonaktifkan ekspansi. Pesannya tampak normal dan tidak ada error yang muncul, sehingga keluaran yang salah sering lolos lama sebelum disadari. Apabila nilai variabel harus masuk ke dalam keluaran, gunakan tanda kutip ganda atau biarkan printf mengambilnya sebagai argumen, misalnya printf 'harga: %s\n' "$HARGA".
Ringkasan#
- Penugasan variabel tidak memuat spasi di sekitar tanda sama dengan, dan nilainya dipanggil melalui
$NAMAatau${NAMA}. - Ekspansi tanpa quoting mengalami pecah kata pada spasi, tab, dan baris baru, lalu hasilnya dikembangkan sebagai glob.
- Tanda kutip ganda memperluas variabel sambil menjaga hasilnya sebagai satu bagian, sedangkan tanda kutip tunggal mempertahankan seluruh isinya apa adanya.
- Menulis setiap variabel di dalam tanda kutip ganda adalah kebiasaan bawaan yang mencegah pecah kata, glob yang tidak disengaja, dan berkas yang diproses salah.
Referensi#
- GNU Bash Reference Manual — Free Software Foundation — dokumentasi resmi bash untuk ekspansi variabel, pecah kata, dan pengembangan pathname.
- Google Shell Style Guide — Google — bagian quoting yang mengatur kapan variabel wajib ditulis di dalam tanda kutip.