Saat kita menekan Ctrl+C, tidak ada tombol ajaib yang menghentikan program — kernel mengirim pesan pendek bernama sinyal (signal) ke proses yang sedang berjalan. Sinyal bisa datang dari kombinasi tombol, perintah kill, atau sistem ketika terminal ditutup. Skrip kita tidak kebal; dihentikan di tengah pekerjaan, skrip bisa meninggalkan file sementara dan pekerjaan setengah jadi. Chapter ini membahas sinyal yang paling sering kita temui, cara mengirim dan menerimanya, serta trap agar skrip berhenti dengan bersih.
Sinyal: Pesan Pendek Antar Proses #
Sinyal adalah notifikasi bernomor dari kernel ke sebuah proses; proses tersebut bereaksi sesuai aksi bawaan tiap sinyal — umumnya mengakhiri diri. Lima yang paling relevan bagi skrip:
| Nama | Nomor | Pemicu umum | Aksi bawaan |
|---|---|---|---|
| SIGHUP | 1 | terminal ditutup | mengakhiri proses |
| SIGINT | 2 | Ctrl+C | mengakhiri proses |
| SIGKILL | 9 | kill -9 | mengakhiri seketika, tak bisa ditangkap |
| SIGTERM | 15 | kill tanpa opsi | mengakhiri proses |
| SIGUSR1 | 10 | ditentukan aplikasi | mengakhiri proses |
Builtin kill di bash bisa menerjemahkan nama dan nomor sinyal dua arah lewat opsi -l:
kill -l 2
kill -l 9
kill -l 15
kill -l TERMOutput yang Dihasilkan:
INT
KILL
TERM
15Tiga baris pertama menerjemahkan nomor menjadi nama; baris terakhir sebaliknya. Daftar lengkap beserta nomornya ada di signal(7) .
Mengirim Sinyal dengan kill #
Perintah kill pid mengirim SIGTERM — sama dengan kill -TERM pid atau kill -15 pid. Untuk proses yang kita mulai sendiri di background, PID-nya tersimpan di variabel khusus $!. Pola lengkapnya: jalankan, simpan PID, kirim sinyal, tunggu:
sleep 30 &
pid=$!
kill -TERM $pid
wait $pid
echo "wait melaporkan: $?"Output yang Dihasilkan:
wait melaporkan: 143Mari kita bedah baris demi baris:
- Baris 1 (
sleep 30 &): menjalankansleepdi background. - Baris 2 (
pid=$!): menyimpan PID proses background terakhir; nilainya beda tiap eksekusi sehingga ditampung, bukan dicetak. - Baris 3 (
kill -TERM $pid): mengirim SIGTERM kepadasleep. - Baris 4-5:
waitmenunggu proses itu selesai, lalu$?melaporkan143— pola128 + 15: proses yang mati karena sinyal N dilaporkan dengan kode128 + N.
trap: Pembersihan yang Pasti Dijalankan #
trap memasang aksi untuk daftar sinyal: trap 'perintah' SINYAL1 SINYAL2 — begitu sinyal itu tiba, skrip menjalankan perintahnya lalu melanjutkan. Nama spesial EXIT berjalan saat skrip berakhir: normal, error, maupun karena sinyal yang tidak dipasangi trap sendiri. Tempat terbaik membersihkan file kerja:
cat > bersih.sh <<'AKHIR'
#!/bin/bash
tmp="pesan.tmp"
trap 'rm -f "$tmp"; echo "pembersihan: $tmp dihapus"' EXIT
printf 'draf pesan\n' > "$tmp"
echo "skrip bekerja, sementara memakai $tmp"
AKHIR
bash bersih.sh
ls pesan.tmp 2>&1
echo "exit code ls: $?"Output yang Dihasilkan:
skrip bekerja, sementara memakai pesan.tmp
pembersihan: pesan.tmp dihapus
ls: cannot access 'pesan.tmp': No such file or directory
exit code ls: 2Mari kita bedah isi bersih.sh baris per baris:
- Baris 2 (
tmp="pesan.tmp"): nama file sementara disimpan ke variabel karena dipakai dua tempat. - Baris 3 (
trap '...' EXIT): aksinyarm -fplusechoketerangan; dipasang sebelum file dibuat agar tidak ada celah. - Baris 4-5: membuat file kerja lalu bekerja dengannya.
Urutan outputnya: pesan kerja tampil dulu, lalu aksi trap begitu skrip keluar, dan baris terakhir membuktikan pesan.tmp sudah tidak ada.
Menangkap Sinyal Khusus di Dalam Skrip #
SIGUSR1 dan SIGUSR2 disediakan untuk keperluan aplikasi — misalnya mencetak kemajuan tanpa menghentikan skrip. Tanpa trap, keduanya membunuh proses. Skrip berikut mengirim USR1 ke dirinya sendiri lewat $$ yang berisi PID skrip:
cat > sinyal.sh <<'AKHIR'
#!/bin/bash
trap 'echo "sinyal USR1 diterima, pekerjaan lanjut"' USR1
echo "skrip mulai"
kill -USR1 $$
echo "skrip selesai"
AKHIR
bash sinyal.shOutput yang Dihasilkan:
skrip mulai
sinyal USR1 diterima, pekerjaan lanjut
skrip selesaiBaris 2 memasang trap USR1; baris 4 mengirimnya ke PID skrip sendiri. Trap mencegat, menjalankan echo, lalu eksekusi lanjut ke baris 5. Mekanisme yang sama bekerja saat Ctrl+C: terminal mengirim SIGINT ke seluruh grup proses foreground, dan trap INT mencegatnya sebelum skrip mati.
SIGKILL yang Tidak Bisa Ditawar #
Perbedaan keduanya soal kesopanan. SIGTERM adalah permintaan agar proses mengakhiri diri — boleh ditangkap, ditunda untuk membersihkan, lalu keluar. SIGKILL dieksekusi langsung oleh kernel: proses tidak diberi kesempatan apa pun — tidak sempat menyimpan file maupun menjalankan trap. Bersama SIGSTOP, keduanya satu-satunya sinyal yang tidak bisa ditangkap, diblok, maupun diabaikan.
Skrip kita kini bisa berhenti tertib dalam kondisi apa pun. Persoalan berikutnya: bagaimana menemukan letak kesalahan ketika skrip berperilaku aneh tetapi diam saja? Peralatan menyusurinya dibahas pada Chapter Debugging Shell Scripts .