Menguasai sintaks dasar Zsh: variabel dan parameter expansion, array yang ternyata 1-indexed, serta daftar perbedaan penting dengan bash seperti tied array $path/$PATH, globbing yang tidak melebar otomatis, dan perbedaan split behavior yang bisa membuat skrip bash berjalan beda di Zsh

Setelah di episode 3 kalian berhasil menjalankan Zsh dan menulis skrip pertama, pada episode ini kita masuk ke fondasi yang sesungguhnya: sintaks dasar dan kompatibilitas dengan bash. Ini adalah episode yang paling sering "menggigit" developer yang baru pindah dari bash — karena Zsh terlihat sama, tetapi ada perbedaan diam-diam yang bisa mengubah perilaku skrip.
Kabar baiknya: mayoritas sintaks bash berjalan langsung di Zsh. Kabar yang perlu kewaspadaan: perbedaan di array, tied variable, dan word splitting bisa membuat skrip yang tadinya berjalan normal di bash tiba-tiba berperilaku aneh di Zsh. Pahami perbedaan-perbedaan ini sekarang, dan kalian akan menghemat berjam-jam debugging di kemudian hari.
Sintaks dasar variabel sama dengan bash:
name="world"
echo "Hello $name"
echo "Hello ${name} lagi"Gunakan ${name} ketika variabel berdampingan dengan karakter lain yang bisa salah ditafsirkan:
prefix="dev"
echo "$prefix_null" # kosong — variabel "prefix_null" tidak ada
echo "${prefix}_null" # "dev_null" — jelas maksudnyaZsh memperkaya parameter expansion dengan modifier yang sangat berguna:
name="belajar-zsh"
echo "${name:l}" # belajar-zsh (huruf kecil)
echo "${name:u}" # BELAJAR-ZSH (huruf besar)
echo "${name:t}" # belajar-zsh (basename, path terakhir)
echo "${name:h}" # . (head, tanpa path terakhir)
file="/etc/hosts"
echo "${file:h}" # /etc
echo "${file:t}" # hosts
echo "${file:r}" # /etc/host (tanpa ekstensi)
v="hello"
echo "${v#h}" # ello (hapus prefix terpendek)
echo "${v%o}" # hell (hapus suffix terpendek)Modifier :t (tail), :h (head), :r (root), :l (lowercase), :u (uppercase) ini adalah perbedaan besar dari bash — tidak ada padanannya di bash tanpa menulis beberapa baris script.
Perbedaan paling membingungkan bagi pendatang dari bash: array Zsh dimulai dari indeks 1, bukan 0.
arr=(a b c)
echo $arr[1] # a
echo $arr[2] # b
echo $arr[-1] # c — indeks negatif dari belakang!
echo ${#arr} # 3 — jumlah elemen
echo ${arr[@]} # a b c — semua elemenSebaliknya di bash arr[0] adalah elemen pertama. Jika kalian menulis echo $arr[0] di Zsh, hasilnya kosong — dan ini silent bug yang menyebalkan. Perhatikan juga $arr[-1] untuk elemen terakhir, fitur yang tidak ada di bash.
nums=(1 2 3 4 5)
echo $nums[2,4] # 2 3 4
echo $nums[-2,-1] # 4 5Slicing dengan range [m,n] juga asli Zsh.
Konsep khas Zsh berikutnya adalah tied array — sepasang variabel skalar dan array yang terhubung. Contoh paling terkenal: $PATH (skalar) dan $path (array). Mengubah salah satu akan mengubah yang lain.
echo $PATH # /usr/local/bin:/usr/bin:/bin
echo $path # /usr/local/bin /usr/bin /bin
# Tambah elemen via array — jauh lebih aman daripada string concat
path+=(/home/$USER/bin)
echo $PATH # ...:/home/devnull/binKeuntungan besar tied array: kalian bisa memanipulasi path sebagai array (append, dedupe, remove) tanpa takut format string salah. Ini kita pakai intensif di episode 15.
Di bash, pattern yang tidak match akan dikirim apa adanya (misal ls *.xyz → ls: cannot access '*.xyz'). Di Zsh, defaultnya error "no matches found":
ls *.xyz
# zsh: no matches found: *.xyzIni sebenarnya feature — lebih aman daripada bash yang mengirim literal. Jika ingin perilaku bash, aktifkan setopt nullglob atau setopt nonomatch (kita bahas di episode 9).
Di bash, $var yang berisi spasi dipecah menjadi beberapa kata secara otomatis di banyak konteks. Di Zsh, parameter expansion tidak melakukan word splitting secara otomatis:
words="satu dua tiga"
for w in $words; do print $w; done # satu (SATU elemen!)Hasilnya hanya satu iterasi karena $words tetap satu string. Untuk memaksa splitting, gunakan ${=words}:
words="satu dua tiga"
for w in ${=words}; do print $w; done
# satu
# dua
# tigaPerilaku ini sebenarnya lebih aman dan less surprising — tetapi wajib diketahui agar skrip bash yang mengandalkan splitting tidak rusak.
Zsh mendukung [[ ]] double bracket (bukan [ ] single bracket) dan menambahkannya dengan operator regex =~ serta pattern matching:
if [[ $file =~ ^/etc ]]; then
print "file di /etc"
fi
if [[ $name == b* ]]; then
print "diawali b"
fiCatatan: Zsh tidak mendukung == dengan pembanding seperti [[ $a == $b ]] sama persis dengan bash untuk semua kasus — beberapa perbedaan kecil di =~ (misal variabel dalam regex perlu di-escape). Selalu uji skrip dengan zsh -n (episode 16).
| Fitur | Bash | Zsh |
|---|---|---|
| Indeks array pertama | 0 | 1 |
$arr[1] artinya | elemen ke-2 | elemen ke-1 |
Word splitting $var | Otomatis (kadang berbahaya) | Tidak otomatis |
| Glob tidak match | Dikirim literal | Error no matches found |
${var:l} lowercase | Tidak ada | Ada |
Tied array $path/$PATH | Tidak ada | Ada |
Warning
Jika kalian menjalankan skrip bash dengan Zsh dan hasilnya aneh, cek tiga hal ini dulu: array (index 1 vs 0), word splitting, dan glob yang gagal. Sebagian besar "skrip bash rusak di zsh" berakar di tiga perbedaan ini.
Karena perbedaan di atas, ada praktik umum: skrip production yang portabel ditulis untuk bash (shebang #!/bin/bash), sementara Zsh dipakai untuk interaktif dan skrip personal. Zsh tetap bisa menjalankan skrip bash yang sederhana, tetapi untuk portabilitas maksimal:
bash script_bash.shInti yang harus dibawa pulang:
${var:l}, ${var:t}, ${var:r}, dll.$arr[-1] dan slicing $arr[m,n].$path/$PATH adalah tied array — manipulasi PATH jadi aman.Di episode 5 selanjutnya kita akan membahas topik yang membuat Zsh legendaris: globbing dan expansions — pattern **/, brace expansion *.{c,h}, negasi ^, ekstensi case-insensitive (#i), dan parameter expansion lanjutan seperti ${name#prefix}. Sampai jumpa di episode 5!