Setiap perintah sudah melapor lewat exit status — kebiasaan yang kita kenal di Chapter Exit Codes pada Section Command Line. Di dalam skrip, laporan itu baru berguna bila ada yang mendengarkannya: skrip yang tidak memeriksa kegagalan akan terus berjalan dengan data setengah jadi, lalu pulang dengan exit status 0 seolah semuanya sukses. Chapter ini mengubah skrip kita menjadi jujur — berhenti di titik gagal dengan exit, memperketat shell dengan set -e dan set -u, serta membersihkan jejaknya lewat trap.
Skrip yang Gagal Secara Diam #
Perhatikan dulu masalahnya. Bila cp gagal di tengah skrip, bash tidak berhenti — ia lanjut ke baris berikutnya dan status akhir skrip ditentukan perintah terakhir, apa pun yang terjadi di tengah. Pola perbaikannya adalah memeriksa langsung dengan if ! lalu mengakhiri dengan exit 1:
cat > salin.sh <<'AKHIR'
#!/bin/bash
if ! cp daftar.txt cadangan.txt; then
echo "salin.sh: daftar.txt tidak ada, berhenti" >&2
exit 1
fi
echo "cadangan berhasil dibuat"
AKHIR
bash salin.sh 2>&1
echo "exit code: $?"Output yang Dihasilkan:
cp: cannot stat 'daftar.txt': No such file or directory
salin.sh: daftar.txt tidak ada, berhenti
exit code: 1Mari kita bedah isi salin.sh baris per barisnya:
- Baris 2 (
if ! cp ...): menjalankancpdan membalik artinya — blokthenberjalan justru ketikacpgagal, karena!membalik exit status menjadi sukses. - Baris 3 (
echo ... >&2): mencetak pesan ke stderr, tempat yang semestinya untuk laporan kegagalan;2>&1pada pemanggilan hanya menggabungkannya agar ikut terekam di output. - Baris 4 (
exit 1): menghentikan skrip seketika dengan kode1— konvensi kegagalan umum. - Baris 6 (
echo "cadangan ..."): hanya tercetak bilacpsukses; pada contoh ini tidak pernah sampai ke sana.
Angka 1 akhirnya terbaca di $? shell pemanggil — pemakai skrip maupun skrip lain kini tahu pekerjaan gagal. Bentuk ringkasnya yang populer adalah pola ||: cp daftar.txt cadangan.txt || { echo "gagal" >&2; exit 1; } — satu baris dengan efek yang sama.
Memperketat Shell dengan set -e #
Memeriksa setiap perintah satu per satu melelahkan untuk skrip panjang. Opsi set -e mengubah sikap bash: begitu ada perintah gagal (exit status bukan nol) di luar kondisi if, bash menghentikan skrip seketika:
cat > otomatis.sh <<'AKHIR'
#!/bin/bash
set -e
echo "langkah 1: menyiapkan data"
cp daftar.txt cadangan.txt
echo "langkah 2: cadangan dibuat"
echo "langkah 3: selesai"
AKHIR
bash otomatis.sh 2>&1
echo "exit code: $?"Output yang Dihasilkan:
langkah 1: menyiapkan data
cp: cannot stat 'daftar.txt': No such file or directory
exit code: 1Tanpa set -e, kedua baris terakhir akan tetap tercetak meski cp gagal. Dengan set -e, skrip berhenti tepat setelah cp gagal dengan kode 1 — baris “langkah 2” dan “langkah 3” tidak pernah dijalankan, terbukti dari output di atas.
Menolak Variabel Kosong dengan set -u #
Kegagalan klasik kedua adalah salah ketik nama variabel: skrip lancar jalan, hanya saja nilainya kosong — dan hasilnya salah secara diam-diam. Opsi set -u menjadikan variabel yang belum didefinisikan sebagai error fatal:
cat > konfig.sh <<'AKHIR'
#!/bin/bash
set -u
echo "nama server: $NAMA_SERVER"
echo "baris ini tidak pernah tercetak"
AKHIR
bash konfig.sh 2>&1
echo "exit code: $?"Output yang Dihasilkan:
konfig.sh: line 3: NAMA_SERVER: unbound variable
exit code: 1Skrip konfig.sh sengaja memanggil $NAMA_SERVER yang tidak pernah didefinisikan. Bash menolaknya dengan pesan unbound variable diikuti nomor baris, lalu menghentikan eksekusi — baris kedua echo tidak pernah tampil. Kombinasi keduanya lazim ditulis sebagai set -eu di baris awal skrip, biasanya berdampingan dengan set -o pipefail yang menjadikan pipeline gagal bila salah satu tahapnya gagal.
Membersihkan Jejak dengan trap EXIT #
Skrip yang bekerja dengan file sementara meninggalkan sampah bila berhenti di tengah jalan — entah karena error, entah karena pengguna menekan Ctrl+C. Perintah trap mendaftarkan perintah penutup yang dijalankan bash pada saat skrip keluar:
cat > lapor.sh <<'AKHIR'
#!/bin/bash
tmp="laporan.tmp"
trap 'rm -f "$tmp"; echo "file sementara dibersihkan"' EXIT
printf 'baris laporan\n' > "$tmp"
echo "mengolah $tmp ..."
wc -l < "$tmp"
AKHIR
bash lapor.sh
lsOutput yang Dihasilkan:
mengolah laporan.tmp ...
1
file sementara dibersihkan
lapor.shBaris trap '...' EXIT memasang perintah pembersihan pada signal khusus EXIT, yang selalu terjadi bagaimana pun skrip berakhir — sukses maupun gagal. Setelah isi laporan selesai diolah, bash menjalankan perintah trap itu sebelum benar-benar keluar: file laporan.tmp dihapus dan pesannya tercetak; ls di baris terakhir membuktikan hanya lapor.sh yang tersisa. Daftar lengkap signal yang bisa ditangkap dibahas pada bagian Shell Parameters and Signals di halaman manual bash(1)
dan kita dalami lagi di Chapter Signals dan Traps.
Skrip kita kini gagal dengan keras dan bersih. Satu celah masih terbuka: sejauh ini skrip menerima input dari kode yang tertulis di dalamnya saja. Chapter Arguments dan Command Line Options membuka pintu itu — membuat skrip menerima data langsung dari baris perintah saat dipanggil.