Jawaban:
Untuk membersihkan beberapa kekacauan, trapbisa digunakan. Itu dapat memberikan daftar hal yang dieksekusi ketika sinyal tertentu tiba:
trap "echo hello" SIGINT
tetapi juga bisa digunakan untuk mengeksekusi sesuatu jika shell keluar:
trap "killall background" EXIT
Itu adalah builtin, jadi help trapakan memberi Anda informasi (bekerja dengan bash). Jika Anda hanya ingin mematikan pekerjaan latar belakang, Anda bisa melakukannya
trap 'kill $(jobs -p)' EXIT
Berhati-hatilah untuk menggunakan tunggal ', agar cangkang tidak $()segera diganti .
kill $(jobs -p)tidak berfungsi dalam tanda hubung, karena menjalankan penggantian perintah dalam sebuah subshell (lihat Substitusi Perintah di man dash)
killall backgroundseharusnya menjadi pengganti? backgroundtidak ada di halaman manual ...
Ini berfungsi untuk saya (ditingkatkan terima kasih kepada para komentator):
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
4.3.30(1)-releaseOSX, dan itu juga dikonfirmasi di Ubuntu . Ada wokaround obvoius , :)
-$$. Ini mengevaluasi ke '- <PID> `misalnya -1234. Di halaman kill kill // builtin manpage tanda hubung utama menentukan sinyal yang akan dikirim. Namun - mungkin memblokir itu, tetapi kemudian dasbor terdepan tidak terdokumentasi sebaliknya. Ada bantuan?
man 2 kill, yang menjelaskan bahwa ketika PID negatif, sinyal dikirim ke semua proses dalam grup proses dengan ID yang disediakan ( en.wikipedia.org/wiki/Process_group ). Ini membingungkan bahwa ini tidak disebutkan dalam man 1 killatau man bash, dan dapat dianggap sebagai bug dalam dokumentasi.
Pembaruan: https://stackoverflow.com/a/53714583/302079 meningkatkan ini dengan menambahkan status keluar dan fungsi pembersihan.
trap "exit" INT TERM
trap "kill 0" EXIT
Mengapa mengonversi INTdan TERMkeluar? Karena keduanya harus memicu kill 0tanpa memasuki loop infinite.
Mengapa memicu kill 0pada EXIT? Karena keluar skrip normal harus dipicukill 0 .
Mengapa kill 0 ? Karena subshells bersarang perlu dibunuh juga. Ini akan mencatat seluruh pohon proses .
kill 0artinya / tidak?
trap 'kill $ (jobs -p)' EXIT
Saya hanya akan membuat sedikit perubahan pada jawaban Johannes dan menggunakan jobs -pr untuk membatasi kill pada proses yang sedang berjalan dan menambahkan beberapa sinyal lagi ke daftar:
trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT
The trap 'kill 0' SIGINT SIGTERM EXITsolusi yang dijelaskan dalam jawaban @ tokland ini benar-benar baik, tapi terbaru Bash crash dengan kesalahan segmantation ketika menggunakannya. Itu karena Bash, mulai dari ay 4.3, memungkinkan rekursi perangkap, yang menjadi tak terbatas dalam kasus ini:
SIGINTatau SIGTERMatau EXIT;kill 0, yang mengirim SIGTERMke semua proses dalam grup, termasuk shell itu sendiri;Ini dapat diatasi dengan secara manual membatalkan pendaftaran perangkap:
trap 'trap - SIGTERM && kill 0' SIGINT SIGTERM EXIT
Cara yang lebih mewah, yang memungkinkan untuk mencetak sinyal yang diterima dan menghindari pesan "Terminated:":
#!/usr/bin/env bash
trap_with_arg() { # from https://stackoverflow.com/a/2183063/804678
local func="$1"; shift
for sig in "$@"; do
trap "$func $sig" "$sig"
done
}
stop() {
trap - SIGINT EXIT
printf '\n%s\n' "recieved $1, killing children"
kill -s SIGINT 0
}
trap_with_arg 'stop' EXIT SIGINT SIGTERM SIGHUP
{ i=0; while (( ++i )); do sleep 0.5 && echo "a: $i"; done } &
{ i=0; while (( ++i )); do sleep 0.6 && echo "b: $i"; done } &
while true; do read; done
UPD : menambahkan contoh minimal; stopfungsi ditingkatkan untuk aviod menghilangkan sinyal yang tidak perlu dan untuk menyembunyikan "Dihentikan:" pesan dari output. Trevor Boyd Smith terima kasih untuk sarannya!
stop()Anda memberikan argumen pertama sebagai nomor sinyal, tetapi kemudian Anda hardcode sinyal apa yang sedang tidak terdaftar. alih-alih hardcode, deregistrasi sinyal Anda bisa menggunakan argumen pertama untuk deregister dalam stop()fungsi (melakukan hal itu berpotensi menghentikan sinyal rekursif lainnya (selain 3 hardcode)).
SIGINT, tetapi kill 0mengirimkan SIGTERM, yang akan terjebak sekali lagi. Ini tidak akan menghasilkan rekursi yang tak terbatas, karena SIGTERMakan terperangkap selama stoppanggilan kedua .
trap - $1 && kill -s $1 0harus bekerja lebih baik. Saya akan menguji dan memperbarui jawaban ini. Terima kasih atas ide bagusnya! :)
trap - $1 && kill -s $1 0akan berfungsi juga, karena kita tidak bisa membunuh EXIT. Tetapi cukup memadai melakukan de-trap TERM, karena killmengirimkan sinyal ini secara default.
EXIT, trappengendali sinyal selalu hanya dieksekusi sekali.
Untuk berada di sisi aman saya merasa lebih baik untuk mendefinisikan fungsi pembersihan dan memanggilnya dari perangkap:
cleanup() {
local pids=$(jobs -pr)
[ -n "$pids" ] && kill $pids
}
trap "cleanup" INT QUIT TERM EXIT [...]
atau menghindari fungsi sama sekali:
trap '[ -n "$(jobs -pr)" ] && kill $(jobs -pr)' INT QUIT TERM EXIT [...]
Mengapa? Karena dengan hanya menggunakan trap 'kill $(jobs -pr)' [...]satu mengasumsikan bahwa akan ada pekerjaan latar belakang berjalan ketika kondisi perangkap diisyaratkan. Ketika tidak ada pekerjaan, seseorang akan melihat pesan berikut (atau serupa):
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
karena jobs -prkosong - saya mengakhiri 'jebakan' itu (pun intended).
[ -n "$(jobs -pr)" ]tidak berfungsi pada bash saya. Saya menggunakan GNU bash, versi 4.2.46 (2) -release (x86_64-redhat-linux-gnu). Pesan "kill: use" terus bermunculan.
jobs -prtidak mengembalikan PID anak-anak dari proses latar belakang. Itu tidak merobohkan seluruh pohon proses, hanya memotong akar.
Versi bagus yang bekerja di Linux, BSD dan MacOS X. Pertama mencoba mengirim SIGTERM, dan jika tidak berhasil, bunuh proses setelah 10 detik.
KillJobs() {
for job in $(jobs -p); do
kill -s SIGTERM $job > /dev/null 2>&1 || (sleep 10 && kill -9 $job > /dev/null 2>&1 &)
done
}
TrapQuit() {
# Whatever you need to clean here
KillJobs
}
trap TrapQuit EXIT
Harap dicatat bahwa pekerjaan tidak termasuk proses anak cucu.
function cleanup_func {
sleep 0.5
echo cleanup
}
trap "exit \$exit_code" INT TERM
trap "exit_code=\$?; cleanup_func; kill 0" EXIT
# exit 1
# exit 0
Seperti https://stackoverflow.com/a/22644006/10082476 , tetapi dengan kode keluar yang ditambahkan
exit_codedatang dari dalam INT TERMperangkap?
jobs -p tidak bekerja di semua shell jika dipanggil dalam sub-shell, mungkin kecuali outputnya dialihkan ke file tetapi bukan pipa. (Saya menganggap itu awalnya ditujukan hanya untuk penggunaan interaktif.)
Bagaimana dengan yang berikut ini:
trap 'while kill %% 2>/dev/null; do jobs > /dev/null; done' INT TERM EXIT [...]
Panggilan ke "pekerjaan" diperlukan dengan shell dasbor Debian, yang gagal memperbarui pekerjaan saat ini ("%%") jika tidak ada.
trap 'echo in trap; set -x; trap - TERM EXIT; while kill %% 2>/dev/null; do jobs > /dev/null; done; set +x' INT TERM EXIT; sleep 100 & while true; do printf .; sleep 1; doneJika Anda menjalankannya di Bash (5.0.3) dan mencoba untuk mengakhiri, tampaknya ada loop tak terbatas. Namun, jika Anda menghentikannya lagi, itu berfungsi. Bahkan dengan Dash (0.5.10.2-6) Anda harus menghentikannya dua kali.
Saya membuat adaptasi jawaban @ tokland yang dikombinasikan dengan pengetahuan dari http://veithen.github.io/2014/11/16/sigterm-propagation.html ketika saya melihat bahwa trapitu tidak memicu jika saya menjalankan proses foreground (tidak dilatar belakangi dengan &):
#!/bin/bash
# killable-shell.sh: Kills itself and all children (the whole process group) when killed.
# Adapted from http://stackoverflow.com/a/2173421 and http://veithen.github.io/2014/11/16/sigterm-propagation.html
# Note: Does not work (and cannot work) when the shell itself is killed with SIGKILL, for then the trap is not triggered.
trap "trap - SIGTERM && echo 'Caught SIGTERM, sending SIGTERM to process group' && kill -- -$$" SIGINT SIGTERM EXIT
echo $@
"$@" &
PID=$!
wait $PID
trap - SIGINT SIGTERM EXIT
wait $PID
Contohnya berfungsi:
$ bash killable-shell.sh sleep 100
sleep 100
^Z
[1] + 31568 suspended bash killable-shell.sh sleep 100
$ ps aux | grep "sleep"
niklas 31568 0.0 0.0 19640 1440 pts/18 T 01:30 0:00 bash killable-shell.sh sleep 100
niklas 31569 0.0 0.0 14404 616 pts/18 T 01:30 0:00 sleep 100
niklas 31605 0.0 0.0 18956 936 pts/18 S+ 01:30 0:00 grep --color=auto sleep
$ bg
[1] + 31568 continued bash killable-shell.sh sleep 100
$ kill 31568
Caught SIGTERM, sending SIGTERM to process group
[1] + 31568 terminated bash killable-shell.sh sleep 100
$ ps aux | grep "sleep"
niklas 31717 0.0 0.0 18956 936 pts/18 S+ 01:31 0:00 grep --color=auto sleep
Hanya untuk keragaman, saya akan memposting variasi https://stackoverflow.com/a/2173421/102484 , karena solusi itu mengarah ke pesan "Dihentikan" di lingkungan saya:
trap 'test -z "$intrap" && export intrap=1 && kill -- -$$' SIGINT SIGTERM EXIT