Saya menulis skrip bash sederhana dengan loop untuk mencetak tanggal dan melakukan ping ke mesin jarak jauh:
#!/bin/bash
while true; do
# *** DATE: Thu Sep 17 10:17:50 CEST 2015 ***
echo -e "\n*** DATE:" `date` " ***";
echo "********************************************"
ping -c5 $1;
done
Ketika saya menjalankannya dari terminal, saya tidak dapat menghentikannya dengan Ctrl+C .
Sepertinya mengirim ^C ke terminal, tetapi skrip tidak berhenti.
MacAir:~ tomas$ ping-tester.bash www.google.com
*** DATE: Thu Sep 17 23:58:42 CEST 2015 ***
********************************************
PING www.google.com (216.58.211.228): 56 data bytes
64 bytes from 216.58.211.228: icmp_seq=0 ttl=55 time=39.195 ms
64 bytes from 216.58.211.228: icmp_seq=1 ttl=55 time=37.759 ms
^C <= That is Ctrl+C press
--- www.google.com ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 40.887/59.699/78.510/18.812 ms
*** DATE: Thu Sep 17 23:58:48 CEST 2015 ***
********************************************
PING www.google.com (216.58.211.196): 56 data bytes
64 bytes from 216.58.211.196: icmp_seq=0 ttl=55 time=37.460 ms
64 bytes from 216.58.211.196: icmp_seq=1 ttl=55 time=37.371 ms
Tidak peduli berapa kali saya menekannya atau seberapa cepat saya melakukannya. Saya tidak bisa menghentikannya.
Lakukan tes dan realisasikan sendiri.
Sebagai solusi sampingan, saya menghentikannya dengan Ctrl+Z , yang menghentikannya dan kemudian kill %1
.
Apa yang sebenarnya terjadi di sini dengan ^C ?
Jawaban yang Diterima:
Apa yang terjadi adalah keduanya bash
dan ping
menerima SIGINT (bash
menjadi tidak interaktif, keduanya ping
dan bash
dijalankan dalam grup proses yang sama yang telah dibuat dan ditetapkan sebagai grup proses latar depan terminal oleh shell interaktif tempat Anda menjalankan skrip tersebut).
Namun, bash
menangani SIGINT itu secara asinkron, hanya setelah perintah yang sedang berjalan telah keluar. bash
hanya keluar setelah menerima SIGINT itu jika perintah yang sedang berjalan mati dari SIGINT (yaitu status keluarnya menunjukkan bahwa perintah itu telah dimatikan oleh SIGINT).
$ bash -c 'sh -c "trap exit\ 0 INT; sleep 10; :"; echo here'
^Chere
Di atas, bash
, sh
dan sleep
menerima SIGINT ketika saya menekan Ctrl-C, tetapi sh
keluar secara normal dengan 0 kode keluar, jadi bash
mengabaikan SIGINT, itulah sebabnya kami melihat "di sini".
ping
, setidaknya yang dari iputils, berperilaku seperti itu. Ketika terputus, ia mencetak statistik dan keluar dengan status keluar 0 atau 1 tergantung pada apakah pingnya dibalas atau tidak. Jadi, ketika Anda menekan Ctrl-C sambil ping
sedang berjalan, bash
perhatikan bahwa Anda telah menekan Ctrl-C
di penangan SIGINT-nya, tetapi sejak ping
keluar secara normal, bash
tidak keluar.
Jika Anda menambahkan sleep 1
di loop itu dan tekan Ctrl-C
saat sleep
sedang berjalan, karena sleep
tidak memiliki handler khusus pada SIGINT, ia akan mati dan melapor ke bash
bahwa itu mati karena SIGINT, dan dalam hal ini bash
akan keluar (ia akan mati sendiri dengan SIGINT untuk melaporkan gangguan tersebut kepada induknya).
Mengapa bash
berperilaku seperti itu, saya tidak yakin dan saya perhatikan perilaku itu tidak selalu deterministik. Saya baru saja mengajukan pertanyaan di bash
milis pengembangan (Pembaruan :@Jilles sekarang telah menemukan alasan dalam jawabannya).
Satu-satunya shell lain yang saya temukan berperilaku serupa adalah ksh93 (Pembaruan, seperti yang disebutkan oleh @Jilles, begitu juga FreeBSD sh
). Di sana, SIGINT tampaknya diabaikan. Dan ksh93
keluar setiap kali perintah dimatikan oleh SIGINT.
Anda mendapatkan perilaku yang sama dengan bash
di atas tetapi juga:
ksh -c 'sh -c "kill -INT \$\$"; echo test'
Tidak menampilkan "tes". Artinya, ia keluar (dengan membunuh dirinya sendiri dengan SIGINT di sana) jika perintah yang ditunggunya mati dari SIGINT, meskipun ia sendiri tidak menerima SIGINT itu.
Solusinya adalah menambahkan:
trap 'exit 130' INT
Di bagian atas skrip untuk memaksa bash
untuk keluar setelah menerima SIGINT (perhatikan bahwa bagaimanapun juga, SIGINT tidak akan diproses secara sinkron, hanya setelah perintah yang sedang berjalan telah keluar).
Idealnya, kami ingin melaporkan kepada orang tua kami bahwa kami meninggal karena SIGINT (sehingga jika itu adalah bash
lain skrip misalnya, bash
. itu skrip juga terganggu). Melakukan exit 130
tidak sama dengan sekarat SIGINT (meskipun beberapa shell akan mengatur $?
ke nilai yang sama untuk kedua kasus), namun sering digunakan untuk melaporkan kematian oleh SIGINT (pada sistem di mana SIGINT adalah yang paling banyak).
Namun untuk bash
, ksh93
atau FreeBSD sh
, itu tidak berhasil. Status keluar 130 itu tidak dianggap sebagai kematian oleh SIGINT dan skrip induk tidak batalkan di sana.
Jadi, alternatif yang mungkin lebih baik adalah bunuh diri dengan SIGINT setelah menerima SIGINT:
trap '
trap - INT # restore default INT handler
kill -s INT "$$"
' INT