Menguasai cara mengoper data dari luar ke dalam skrip melalui positional parameters ($1, $2, ${10}), memahami special parameters ($0, $#, $@, $*, $, $?), serta memakai shift untuk mengonsumsi argumen secara berurutan beserta jebakan-jebakan yang paling sering membuat skrip gagal diam-diam.

Setelah di episode 4 sebelumnya kita membahas quoting, escaping & word splitting — bagaimana kutip tunggal, kutip ganda, dan backslash mengendalikan cara bash memecah string menjadi kata-kata, serta mengapa "$@" lebih aman daripada $@ — pada episode kali ini kita naik satu tingkat: mengoper data dari luar ke dalam skrip.
Sebuah skrip yang hanya bekerja dengan nilai yang diketik langsung di dalam kodenya sama seperti mesin kasir yang hanya bisa menghitung nominal "10000" terus-menerus. Tidak berguna, bukan? Untuk itu, kita perlu mempelajari bagaimana skrip menerima argument baris perintah (command line arguments) dan memanfaatkan special parameters bawaan bash. Ini adalah fondasi yang membuat skrip kalian bisa dipakai berulang kali dengan data yang berbeda — tanpa mengubah satu baris kode pun.
Perhatikan perintah yang sudah sering kalian pakai: cp file.txt backup/, rm -rf cache/, systemctl restart nginx. Ketiga perintah itu tidak meminta data lewat dialog atau menu — mereka menerima argumen langsung di baris perintah. Inilah paradigma Unix: perintah kecil yang serba guna, dikonfigurasi lewat argumen, bukan perintah raksasa yang menanyakan semuanya satu per satu.
Bayangkan kalian membuat skrip backup.sh untuk mem-backup direktori. Ada dua cara menuliskannya:
#!/usr/bin/env bash
tar -czf /home/arman/backup.tar.gz /home/arman/Projects
echo "Backup selesai."Versi pertama hanya bisa mem-backup satu folder, selamanya. Versi kedua bisa dipakai untuk folder apa pun: ./backup.sh /tmp/backup.tar.gz /home/arman/Projects. Inilah mengapa kita belajar argumen — agar skrip menjadi bahan yang dapat digunakan ulang (reusable), bukan barang sekali pakai.
$1, $2, $3, ..., ${10}Bash menandai argumen pertama sebagai $1, argumen kedua $2, ketiga $3, dan seterusnya. Mereka disebut positional parameters karena posisinya dalam perintah menentukan nilainya. Jalankan skrip berikut dengan beberapa argumen:
#!/usr/bin/env bash
echo "Aksi : $1"
echo "Objek : $2"
echo "Lokasi : $3"./skrip_cerita.sh install nginx server-01Aksi : install
Objek : nginx
Lokasi : server-01Setiap argumen dipisahkan oleh whitespace saat kalian mengetik. Jika satu argumen mengandung spasi (misalnya My Documents), kalian harus membungkusnya dengan tanda kutip: ./skrip_cerita.sh install "My App" server-01 — sehingga "My App" dihitung sebagai satu argumen, bukan dua. Ingat pelajaran quoting di episode 4: kutip adalah cara kita "menempelkan" beberapa kata menjadi satu.
${10}, bukan $10Ini salah satu jebakan paling klasik dan paling sulit dilacak. Untuk argumen ke-10 ke atas, kalian wajib memakai kurung kurawal: ${10}, ${11}, dan seterusnya. Kenapa? Karena $10 dibaca bash sebagai $1 yang diikuti karakter literal 0. Coba lihat:
#!/usr/bin/env bash
echo "Argumen ke-1 : $1"
echo "Argumen ke-10 : ${10}"./skrip_dengan_banyak_argumen.sh a b c d e f g h i JArgumen ke-1 : a
Argumen ke-10 : JSekarang bayangkan kalau kita salah menulis $10:
echo "Argumen ke-10 : $10"
echo "Argumen ke-11 : $11"Argumen ke-10 : a0Bash menampilkan nilai $1 (yaitu a) lalu menambahkan karakter 0 di belakangnya — tanpa error sama sekali. Inilah jenis bug yang paling berbahaya: gagal secara diam-diam (silent failure). Kalian melihat output "a0", tidak ada pesan error, dan mungkin membuang waktu berjam-jam mencarinya. Selalu gunakan ${10} ke atas, dan biasakan memakai kurung kurawal untuk semua positional parameter — aman dan konsisten.
$0, $#, $@, $*, $$, $?Selain positional parameters, bash menyediakan kumpulan parameter khusus yang ditandai simbol tunggal. Mereka bukan argumen, melainkan "metadata" yang diberikan bash kepada setiap skrip:
| Parameter | Arti | Contoh Penggunaan |
|---|---|---|
$0 | Nama skrip yang sedang dijalankan | Menampilkan nama skrip dalam pesan error/usage |
$# | Jumlah argumen (bukan nilainya) | if [ $# -eq 0 ]; then → cek tanpa argumen |
$@ | Semua argumen sebagai kata terpisah | for arg in "$@"; do → loop per argumen |
$* | Semua argumen sebagai satu string | "$*" → gabungkan semua argumen dengan spasi |
$$ | PID (Process ID) dari skrip ini | Membuat nama file temp unik: /tmp/script_$$.log |
$? | Exit status perintah terakhir | command; if [ $? -eq 0 ]; then ... |
$0 — Nama Skrip$0 menyimpan nama skrip yang sedang berjalan. Ini berguna untuk dua hal: (1) menampilkan pesan usage yang benar saat argumen salah, dan (2) membuat pesan error yang jelas. Perhatikan pola ini di hampir semua skrip profesional:
#!/usr/bin/env bash
if [ $# -lt 2 ]; then
echo "Usage: $0 <nama> <npm>" >&2
exit 1
fi$# — Jumlah Argumen$# menghitung berapa banyak argumen yang diberikan, bukan nilainya. Jika ./skrip.sh a b c, maka $# bernilai 3. Ini adalah alat validasi nomor satu: sebelum memakai $1, $2, cek dulu bahwa $# cukup. Mengapa? Karena membaca $1 yang tidak ada akan menghasilkan string kosong — skrip terus berjalan tanpa error, tetapi dengan data kosong.
$? — Exit Status Perintah TerakhirSetiap perintah di bash meninggalkan exit status: 0 berarti sukses, selain 0 (biasanya 1-255) berarti gagal. $? selalu berisi exit status dari perintah yang terakhir dijalankan. Ini adalah bahasa yang dipakai Linux untuk berkomunikasi dengan skrip: apakah pekerjaanmu berhasil?
ls /tmp/siap-produksi
echo "Exit status: $?"ls: cannot access '/tmp/siap-produksi': No such file or directory
Exit status: 2Perhatikan: karena ls gagal, exit status-nya bukan 0. Di episode 9 nanti kita akan membahas bagaimana pola ini menjadi jantung dari seluruh logika if di bash. Tapi satu hal wajib diingat sejak sekarang: semua di bash adalah soal exit status.
$$ — PID dari Skrip$$ berisi Process ID dari proses yang menjalankan skrip. PID adalah identitas unik setiap proses di Linux (ingat konsep /proc di series Linux). Untuk apa berguna? Membuat nama file sementara yang unik:
log_tmp="/tmp/install_$$.log"
echo "Logging ke $log_tmp"Logging ke /tmp/install_12345.logKarena setiap proses punya PID berbeda, dua skrip yang berjalan bersamaan tidak akan saling menimpa file temp — analog dengan dua pasien di rumah sakit yang punya nomor rekam medis berbeda, sehingga berkas mereka tidak pernah tertukar.
$@ vs $*: Perbedaan yang Menentukan Hidup-MatiDua parameter ini sama-sama merepresentasikan "semua argumen", tetapi perilakunya berbeda drastis — dan inilah salah satu sumber bug paling sering di bash. Ingat aturan emasnya:
"$@" — setiap argumen tetap menjadi kata terpisah. Spasi di dalam satu argumen dipertahankan."$*" — semua argumen digabung menjadi satu string, dipisahkan spasi (karakter pertama dari IFS).Perhatikan perbedaannya dengan sebuah eksperimen. Buat skrip yang mencetak setiap argumen dalam tanda kurung, agar kita bisa melihat batas antar argumen:
#!/usr/bin/env bash
echo "Dengan \"\$@\":"
for arg in "$@"; do
echo " [\$arg]"
done
echo "Dengan \"\$*\":"
for arg in "$*"; do
echo " [\$arg]"
done./show_args.sh "file penting.txt" "laporan 2026"Dengan "$@":
[file penting.txt]
[laporan 2026]
Dengan "$*":
[file penting.txt laporan 2026]Perhatikan baris terakhir: "$*" menyatukan kedua argumen menjadi satu kata. Bagi loop for, hasilnya cuma satu iterasi — padahal kalian ingin dua file diproses. Kesimpulan praktis: selalu gunakan "$@" untuk "meneruskan semua argumen". "$*" jarang dibutuhkan — hanya saat kalian memang ingin menggabungkan argumen menjadi satu string (misalnya untuk pesan log).
Tip
Cara cepat mengingat perbedaannya: "$@" mempertahankan batas antar argumen, sedangkan "$*" menghapus semua batas. Bayangkan "$@" sebagai lima kantong belanja terpisah yang dibawa pulang — isinya tidak tercampur — sementara "$*" menuangkan semuanya ke satu keranjang besar. Jika kalian meneruskan argumen antar skrip atau ke for loop, kalian hampir selalu butuh kantong-kantong terpisah: "$@".
Ada kombinasi yang lebih berbahaya lagi: $@ dan $* tanpa tanda kutip. Tanpa kutip, word splitting berlaku (ingat episode 4): setiap argumen akan dipecah ulang berdasarkan spasi. Argumen "file penting.txt" yang tadinya utuh akan dipecah menjadi file, penting.txt, dan txt. Inilah kenapa for f in $@; do dianggap code smell — gunakan for f in "$@"; do.
Important
Aturan yang tidak bisa ditawar: selalu tulis "$@" — dengan tanda kutip. Saat kalian meneruskan argumen ke perintah lain (misalnya script_kedua.sh "$@"), cara inilah yang menjaga setiap argumen tetap utuh apa pun isinya, termasuk spasi dan karakter aneh. Skrip yang menulis $@ tanpa kutip akan hancur begitu menerima nama file yang mengandung spasi — dan di dunia nyata, nama file ber-spasi itu sangat umum.
shift: Mengonsumsi Argumen Secara BerurutanKadang kalian tidak tahu berapa banyak argumen yang akan datang, atau ingin memprosesnya satu per satu dari depan. Di sinilah shift berperan: shift membuang $1, lalu menggeser semua argumen satu posisi ke kiri. $2 menjadi $1, $3 menjadi $2, dan $# berkurang satu.
#!/usr/bin/env bash
while [ $# -gt 0 ]; do
echo "Memproses: $1"
shift
done
echo "Semua argumen selesai diproses."./proses.sh api server webMemproses: api
Memproses: server
Memproses: web
Semua argumen selesai diproses.Mengapa pola ini begitu penting? Karena memungkinkan skrip memproses jumlah argumen tak terbatas tanpa harus menulis $1 sampai $100. Analoginya seperti kasir yang melayani antrean pembeli: selama antrean masih ada ($# -gt 0), ia melayani pembeli paling depan ($1), lalu pembeli berikutnya maju (shift). Di episode yang membahas getopts nanti, pola ini akan menjadi fondasi untuk membuat CLI profesional.
Perhatikan juga kondisi loop di atas: [ $# -gt 0 ]. Ini adalah test — kita baru akan mempelajari test operator secara mendalam di episode 9, tapi untuk sekarang cukup pahami bahwa pernyataan itu berbunyi "selama jumlah argumen masih lebih besar dari nol, lanjutkan".
Saatnya merangkai semuanya. Kita akan membangun skrip kecil yang menyapa user, memvalidasi jumlah dan format argumen, serta memanfaatkan $0 untuk pesan usage yang jelas:
#!/usr/bin/env bash
set -euo pipefail
if [ $# -ne 2 ]; then
echo "Usage: $0 <nama> <npm>" >&2
exit 1
fi
nama="$1"
npm="$2"
echo "Halo, $nama!"
echo "Selamat datang di sesi latihan NPM $npm."Mari bedah baris per baris:
| Baris | Yang Terjadi | Mengapa |
|---|---|---|
#!/usr/bin/env bash | Shebang — menunjuk interpreter bash | Skrip dijalankan dengan bash, bukan shell lain |
set -euo pipefail | Strict mode: berhenti saat error, variabel kosong, pipeline gagal | Mencegah skrip "melanjutkan" saat sudah salah — dibahas mendalam di episode error handling |
[ $# -ne 2 ] | Test: jumlah argumen tidak sama dengan 2 | Jika user lupa argumen, jangan lanjut — beri tahu cara pakai |
$0 <nama> <npm> | Pesan usage memakai nama skrip yang sebenarnya | User tahu persis perintah apa yang harus ia ketik |
>&2 | Redirect pesan ke stderr | Pesan error harus ke stderr, bukan stdout — agar bisa dipisahkan saat di-pipe |
exit 1 | Hentikan skrip dengan exit status gagal | Penelepon skrip (misal CI) tahu bahwa skrip gagal |
./greet.sh
./greet.sh Arman
./greet.sh Arman 202601001Usage: ./greet.sh <nama> <npm>
Usage: ./greet.sh <nama> <npm>
Halo, Arman!
Selamat datang di sesi latihan NPM 202601001.Perhatikan bahwa ./greet.sh dan ./greet.sh Arman menghasilkan pesan usage — karena keduanya tidak memberi dua argumen yang dijanjikan. Skrip yang baik menolak bekerja dengan data tidak lengkap, dan memberikan petunjuk yang jelas. Ini kebiasaan yang akan sangat dihargai saat skrip kalian dipakai orang lain (termasuk "diri kalian enam bulan kemudian").
$10 alih-alih ${10}Sudah dibahas di atas, tapi layak diulang sebagai peringatan nomor satu: $10 adalah $1 + 0. Skrip tidak error, tapi outputnya salah total. Selalu gunakan ${10} dan seterusnya.
$@ tanpa tanda kutipfor f in $@ akan memecah argumen ber-spasi menjadi potongan-potongan. Di mesin produksi yang penuh nama file seperti hasil_rekap 2026.xlsx, ini adalah bencana. Tulis selalu "$@".
shiftPola while [ $# -gt 0 ]; do ...; shift; done sudah benar karena kondisi dicek sebelum setiap iterasi. Kesalahan umum adalah memindahkan shift ke akhir tanpa kondisi yang tepat — atau menulis while [ $# -ge 0 ], yang membuat loop berjalan sekali lagi ketika $# sudah 0, sehingga $1 menjadi kosong. Ingat: gunakan -gt 0, bukan -ge 0.
$# sebelum membaca $1Membaca $1 saat tidak ada argumen menghasilkan string kosong — skrip berjalan "normal" dengan data kosong, dan error baru muncul jauh di tengah proses (misalnya saat membuat file kosong). Selalu validasi $# lebih dulu. Mencegah lebih mudah daripada mendeteksi.
exit 1 setelah pesan usageJika skrip menampilkan pesan usage lalu melanjutkan bekerja dengan argumen yang hilang, itu lebih buruk daripada tidak memvalidasi sama sekali. Selalu akhiri jalur validasi gagal dengan exit 1 (atau status non-nol yang sesuai).
Pada episode 5 ini, kita telah mempelajari bagaimana skrip menerima perintah dari dunia luar: positional parameters $1, $2, hingga ${10} yang membedakan posisi argumen; special parameters $0 (nama skrip), $# (jumlah argumen), $? (exit status terakhir), $$ (PID), serta perbedaan krusial "$@" vs "$*"; dan shift untuk mengonsumsi argumen secara berurutan. Semua itu kita rangkai menjadi skrip greet.sh yang memvalidasi argumennya sendiri.
Inti yang harus dibawa pulang:
${10} ke atas wajib memakai kurung kurawal; $10 adalah bug tersembunyi."$@" menjaga setiap argumen tetap utuh; "$*" menggabungkannya — selalu pilih "$@".$? adalah bahasa komunikasi antara perintah dan skrip: 0 = sukses.$# sebelum membaca argumen, dan akhiri kegagalan dengan exit 1.Namun, skrip yang menerima perintah dari command line hanya satu pintu masuk data. Ada pintu lain yang tidak kalah penting: input dari user secara interaktif. Di episode 6 selanjutnya, kita akan membahas User Input Interaktif & Menu dengan read — menguasai perintah read beserta flag -p, -s, -t, -n, -a, membangun menu interaktif dengan select, hingga pola validasi input yang kebal terhadap input kosong dan salah format. Sampai jumpa di episode berikutnya!