Saya mencari cara untuk membersihkan kekacauan ketika skrip tingkat atas saya keluar.
Terutama jika saya ingin menggunakan set -e
, saya berharap proses latar belakang akan mati ketika skrip keluar.
Untuk membersihkan beberapa kekacauan, trap
bisa 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 trap
akan 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 background
seharusnya menjadi pengganti?background
tidak ada di halaman manual ...Ini berfungsi untuk saya (ditingkatkan terima kasih kepada para komentator):
kill -- -$$
mengirimkan SIGTERM ke seluruh kelompok proses, sehingga membunuh juga keturunan.Menentukan sinyal
EXIT
berguna saat menggunakanset -e
(lebih detail di sini ).sumber
4.3.30(1)-release
OSX, 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 dalamman 1 kill
atauman bash
, dan dapat dianggap sebagai bug dalam dokumentasi.Pembaruan: https://stackoverflow.com/a/53714583/302079 meningkatkan ini dengan menambahkan status keluar dan fungsi pembersihan.
Mengapa mengonversi
INT
danTERM
keluar? Karena keduanya harus memicukill 0
tanpa memasuki loop infinite.Mengapa memicu
kill 0
padaEXIT
? Karena keluar skrip normal harus dipicukill 0
.Mengapa
kill 0
? Karena subshells bersarang perlu dibunuh juga. Ini akan mencatat seluruh pohon proses .sumber
kill 0
artinya / tidak?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:
sumber
The
trap 'kill 0' SIGINT SIGTERM EXIT
solusi 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:SIGINT
atauSIGTERM
atauEXIT
;kill 0
, yang mengirimSIGTERM
ke semua proses dalam grup, termasuk shell itu sendiri;Ini dapat diatasi dengan secara manual membatalkan pendaftaran perangkap:
Cara yang lebih mewah, yang memungkinkan untuk mencetak sinyal yang diterima dan menghindari pesan "Terminated:":
UPD : menambahkan contoh minimal;
stop
fungsi ditingkatkan untuk aviod menghilangkan sinyal yang tidak perlu dan untuk menyembunyikan "Dihentikan:" pesan dari output. Trevor Boyd Smith terima kasih untuk sarannya!sumber
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 dalamstop()
fungsi (melakukan hal itu berpotensi menghentikan sinyal rekursif lainnya (selain 3 hardcode)).SIGINT
, tetapikill 0
mengirimkanSIGTERM
, yang akan terjebak sekali lagi. Ini tidak akan menghasilkan rekursi yang tak terbatas, karenaSIGTERM
akan terperangkap selamastop
panggilan kedua .trap - $1 && kill -s $1 0
harus bekerja lebih baik. Saya akan menguji dan memperbarui jawaban ini. Terima kasih atas ide bagusnya! :)trap - $1 && kill -s $1 0
akan berfungsi juga, karena kita tidak bisa membunuhEXIT
. Tetapi cukup memadai melakukan de-trapTERM
, karenakill
mengirimkan sinyal ini secara default.EXIT
,trap
pengendali sinyal selalu hanya dieksekusi sekali.Untuk berada di sisi aman saya merasa lebih baik untuk mendefinisikan fungsi pembersihan dan memanggilnya dari perangkap:
atau menghindari fungsi sama sekali:
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):karena
jobs -pr
kosong - saya mengakhiri 'jebakan' itu (pun intended).sumber
[ -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 -pr
tidak 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.
Harap dicatat bahwa pekerjaan tidak termasuk proses anak cucu.
sumber
Seperti https://stackoverflow.com/a/22644006/10082476 , tetapi dengan kode keluar yang ditambahkan
sumber
exit_code
datang dari dalamINT TERM
perangkap?Opsi lain adalah membuat skrip mengatur dirinya sebagai pemimpin grup proses, dan menjebak killpg pada grup proses Anda saat keluar.
sumber
Jadi naskah memuat skrip. Jalankan perintah
killall
(atau apa pun yang tersedia di OS Anda) yang dijalankan segera setelah skrip selesai.sumber
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:
Panggilan ke "pekerjaan" diperlukan dengan shell dasbor Debian, yang gagal memperbarui pekerjaan saat ini ("%%") jika tidak ada.
sumber
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; done
Jika 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
trap
itu tidak memicu jika saya menjalankan proses foreground (tidak dilatar belakangi dengan&
):Contohnya berfungsi:
sumber
Hanya untuk keragaman, saya akan memposting variasi https://stackoverflow.com/a/2173421/102484 , karena solusi itu mengarah ke pesan "Dihentikan" di lingkungan saya:
sumber