Mengelola environment Zsh dengan benar: perbedaan $PATH dan $path sebagai tied array, dedupe otomatis dengan typeset -U, guard [ -d ] sebelum menambah direktori ke path, ekspor variabel environment, serta modul zsh/datetime dan zsh/mathfunc untuk skrip yang butuh waktu dan kalkulasi numerik

Setelah di episode 14 kalian memahami async, jobs, dan piping, pada episode ini kita membahas bagian yang paling sering diremehkan tetapi paling sering menjadi sumber bug aneh: environment dan manajemen PATH. Pesan error seperti command not found di cron, atau skrip yang berjalan saat manual tapi gagal di scheduler, hampir selalu berakar pada manajemen environment yang salah.
Zsh memberi alat yang tidak dimiliki bash untuk masalah ini: tied array ($PATH/$path), typeset -U untuk dedupe otomatis, dan modul seperti zsh/datetime dan zsh/mathfunc. Kuasai bab ini dan kalian akan menyelesaikan 90% masalah "kenapa skripnya beda hasil" dalam karier kalian.
Ingat tied array dari episode 4: $PATH adalah skalar (string dengan :), $path adalah array. Keduanya terhubung — mengubah satu mengubah yang lain.
print $PATH
print -l $pathManipulasi path sebagai array jauh lebih aman:
path=(~/bin $path)
path+=($HOME/.local/bin)
export PATHPerhatikan: setelah mengubah path, perlu export PATH agar shell anak dan skrip melihatnya (karena path saja tidak diwariskan). Pola yang banyak dipakai:
export PATH="$HOME/.local/bin:$PATH"Kesalahan umum: menambahkan path yang sama berulang kali (misal setiap sesi). Zsh punya solusi elegan: typeset -U membuat array hanya menyimpan nilai unik — duplikat otomatis dibuang saat ditambahkan.
typeset -U path
path+=(~/bin)
path+=(~/bin) # sudah ada, diabaikan
print -l $path # ~/bin hanya sekaliIni fitur yang sangat dicintai: tidak perlu menulis guard manual untuk memeriksa keberadaan duplikat. typeset -U juga umum dipakai untuk cdpath, fpath, dan manpath.
Tambah path yang tidak ada = command not found yang membingungkan, dan path yang salah bisa "menang" dari path yang benar. Guard standar:
add_to_path() {
local dir="$1"
if [[ -d "$dir" && -r "$dir" ]]; then
path+=("$dir")
export PATH
fi
}
add_to_path "$HOME/.local/bin"
add_to_path "$HOME/go/bin"
add_to_path "/opt/scripts"Dengan guard ini, direktori yang belum ada tidak masuk PATH — mencegah typos dan mencegah path dari tool yang belum diinstall ikut "mengacaukan" lookup.
Tip
Kombinasi terbaik: typeset -U path (dedupe) + function add_to_path dengan guard [ -d ] (validasi). Ini pattern yang dipakai banyak dotfiles profesional dan menghilangkan seluruh kelas bug PATH.
Environment yang diwariskan ke proses anak harus di-export. Dua cara:
# cara 1: set lalu export
EDITOR=nvim
export EDITOR
# cara 2: export langsung
export LANG=en_US.UTF-8
export KUBECONFIG="$HOME/.kube/config"
# cara 3: export + assignment satu baris (khas zsh)
export DB_HOST=localhost DB_PORT=5432Untuk menyetel variabel hanya untuk satu perintah, prefix perintah:
DEBUG=1 ./deploy.sh
KUBE_CONTEXT=prod kubectl get podsIni tidak mencemari environment sesi — sangat berguna saat debug dan testing.
Ingat startup files di episode 2. Variabel environment yang harus ada di semua mode (termasuk skrip non-interaktif) sebaiknya ditaruh di ~/.zshenv, bukan .zshrc:
# ~/.zshenv
export EDITOR=nvim
export LANG=C.UTF-8
export HISTFILE="$HOME/.zsh_history"zsh -c 'print $EDITOR' akan membaca zshenv — tetapi zsh -i -c (interaktif) akan membaca keduanya. Ini mencegah bug "variabel tidak ada di cron" yang menyiksa.
Warning
Jangan menaruh prompt, alias, atau binding di zshenv — ia dibaca oleh shell non-interaktif juga, sehingga skrip bisa "kotor" dengan function yang tidak perlu. Aturan emas: zshenv hanya variabel dan fungsi yang benar-benar dibutuhkan di mana-mana.
Modul zsh/datetime menyediakan variabel EPOCHSECONDS (waktu Unix) dan function strftime bawaan:
zmodload zsh/datetimeprint $EPOCHSECONDS # 1755385200 (epoch)
strftime '%Y-%m-%d %H:%M:%S' $EPOCHSECONDS
# timestamp untuk nama file backup
backup_name="backup_$(strftime '%Y%m%d_%H%M%S').tar.gz"
print $backup_name
# math dengan epoch
start=$EPOCHSECONDS
sleep 2
print "Durasi: $((EPOCHSECONDS - start)) detik"Ini mengalahkan memanggil binary date eksternal di dalam loop — EPOCHSECONDS adalah variabel, tidak ada proses fork. Untuk skrip yang mengukur durasi atau membuat timestamp, ini pola yang tepat.
Modul zsh/mathfunc menyediakan fungsi matematika untuk (( )) dan $(( )):
zmodload zsh/mathfuncprint $(( 2.5 ** 3 )) # 15.625
print $(( sin(3.14) )) # ~0.00159
print $(( ceil(2.3) )) # 3
print $(( sqrt(16) )) # 4zmodload zsh/mathfunc
usage=$(du -sk /var/log | awk '{print $1}')
print "Ukuran dalam GB: $(( usage / 1024.0 ))"Zsh 5.9 mendukung floating point arithmetic native di (( )). Perpaduan zsh/mathfunc dan EPOCHSECONDS membuat skrip bisa melakukan timing, kalkulasi persentase, dan konversi unit tanpa tool eksternal.
Modul zsh/parameter memberikan akses terprogram ke struktur shell — array associative yang berisi info tentang parameter, fungsi, alias, dll:
zmodload zsh/parameterprint ${(k)aliases} # semua alias
print ${(k)functions} # semua function yang terdefinisi
print $parameters[PATH] # skalar/array/tied
print $functions[compinit] # isi definisi compinitBerguna untuk tooling dan debug: misalnya memeriksa apakah sebuah function sudah dimuat, atau mendaftar semua alias untuk dokumentasi otomatis.
path diubah tapi tidak di-export; shell anak tidak melihat perubahan..zshrc menimpa PATH=... (bukan menambah), semua perintah dasar hilang; selalu gunakan += atau prefix.typeset -U path.[[ -d "$dir" ]] seperti di atas.zsh/datetime tidak aktif — EPOCHSECONDS tidak terdefinisi sampai zmodload zsh/datetime dijalankan.Inti yang harus dibawa pulang:
$PATH skalar, $path array — tied array; manipulasi via array, jangan lupa export PATH.typeset -U path menghapus duplikat otomatis.[[ -d ]] sebelum menambah direktori ke path.zmodload zsh/datetime (EPOCHSECONDS, strftime) dan zsh/mathfunc (sin, ceil, float) untuk skrip yang butuh waktu dan kalkulasi.Di episode 16 selanjutnya kita akan membahas error handling & debugging — menangkap error dengan trap (ERR/EXIT), opsi errexit, memahami exit codes, lalu debugging dengan zsh -x, zsh -n, print -P/print -l, dan which -f. Sampai jumpa di episode 16!