Yanıtlar:
Biraz karışıklığı temizlemek trapiçin kullanılabilir. Belirli bir sinyal geldiğinde yürütülen şeylerin bir listesini sağlayabilir:
trap "echo hello" SIGINT
ancak kabuk çıkarsa bir şeyi yürütmek için de kullanılabilir:
trap "killall background" EXIT
Bu bir yerleşiktir, bu yüzden help trapsize bilgi verecektir (bash ile çalışır). Yalnızca arka plandaki işleri öldürmek istiyorsanız,
trap 'kill $(jobs -p)' EXIT
'Kabuğun $()hemen yerine geçmesini önlemek için tekli kullanmaya dikkat edin.
kill $(jobs -p)alt kabukta komut ikamesi yürüttüğü için tire içinde çalışmaz (bkz. man
killall backgroundbir yer tutucu olması gerekiyordu? backgroundadam sayfasında değil ...
Bu benim için çalışıyor (yorumcular sayesinde geliştirildi):
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
4.3.30(1)-releaseOSX'te çalıştırdım ve Ubuntu'da da onaylandı . Yine de bir obvoius wokaround var :)
-$$. '- <PID> `olarak değerlendirilir örn -1234. Kill manpage // builtin manpage'de, önde gelen bir çizgi gönderilecek sinyali belirtir. Bununla birlikte - muhtemelen bunu engeller, ancak daha sonra önde gelen çizgi başka türlü belgelenmez. Herhangi bir yardım?
man 2 killBir PID negatif olduğunda, sinyalin işlem grubundaki tüm işlemlere sağlanan kimliğe ( en.wikipedia.org/wiki/Process_group ) gönderildiğini açıklayan Check . Bunun belirtilmemesi man 1 killveya man bashbelgelerinde bir hata olarak düşünülmesi kafa karıştırıcıdır .
Güncelleme: https://stackoverflow.com/a/53714583/302079 çıkış durumu ve bir temizleme işlevi ekleyerek bunu iyileştirir.
trap "exit" INT TERM
trap "kill 0" EXIT
Neden dönüşüm INTve TERMçıkış? Çünkü her ikisi de kill 0sonsuz döngüye girmeden tetiklemelidir .
Neden tetik kill 0üzerinde EXIT? Çünkü normal komut dosyası çıkışları da tetiklenmelidir kill 0.
Neden kill 0? Çünkü iç içe geçmiş alt kabukların da öldürülmesi gerekiyor. Bu işlem ağacının tamamını kapatacaktır .
kill 0anlama geldiği / ne anlama geldiği hakkında daha fazla bilgi verebilir misiniz ?
tuzağı 'kill $ (jobs -p)' EXIT
Johannes'in cevabında sadece küçük değişiklikler yapardım ve kill'i çalışan süreçlerle sınırlamak ve listeye birkaç sinyal daha eklemek için işler -pr kullanırım:
trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT
trap 'kill 0' SIGINT SIGTERM EXITAçıklanan çözüm @ tokland cevabı gerçekten güzel, ama son Bash bir segmantation arıza ile çöküyor onu kullanırken. Çünkü Bash, v. 4.3'ten başlayarak, bu durumda sonsuz olan tuzak özyinelemesine izin verir:
SIGINTveya SIGTERMveya EXIT;kill 0gönderen, yakalanır SIGTERM;Bu, tuzağın manuel olarak kaydedilmesiyle çözülebilir:
trap 'trap - SIGTERM && kill 0' SIGINT SIGTERM EXIT
Alınan sinyalin yazdırılmasını sağlayan ve "Sonlandırıldı:" iletilerini önleyen daha süslü yol:
#!/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 : minimal örnek eklendi; stopgereksiz sinyallerin ayrılmasını önleme ve "Sonlandırıldı:" iletilerini çıkıştan gizleme işlevi geliştirildi . Öneriler için Trevor Boyd Smith'e teşekkürler !
stop()ilk bağımsız değişkeni sinyal numarası olarak sağlarsınız, ancak daha sonra hangi sinyallerin kaydının kaldırıldığını kodlarsınız. kayıttan kaldırılan sinyallerin sabit kodundan ziyade, stop()fonksiyonda kayıttan çıkmak için ilk argümanı kullanabilirsiniz .
SIGINT, ancak bir kez daha tuzağa düşecek kill 0gönderir SIGTERM. Bu sonsuz bir özyineleme üretmeyecektir, çünkü SIGTERMikinci stopçağrı sırasında tuzağa düşecektir .
trap - $1 && kill -s $1 0daha iyi çalışmalıdır. Bu cevabı test edip güncelleyeceğim. Güzel fikir için teşekkürler! :)
trap - $1 && kill -s $1 0öldüremeyeceğimiz için de çalışmaz EXIT. Ancak tuzağa düşürmek gerçekten yeterlidir TERM, çünkü killbu sinyali varsayılan olarak gönderir.
EXIT, trapsinyal işleyici her zaman sadece bir kez yürütülür.
Güvenli tarafta olmak için bir temizleme fonksiyonu tanımlamak ve onu tuzaktan çağırmak daha iyi olur:
cleanup() {
local pids=$(jobs -pr)
[ -n "$pids" ] && kill $pids
}
trap "cleanup" INT QUIT TERM EXIT [...]
veya işlevden tamamen kaçınmak:
trap '[ -n "$(jobs -pr)" ] && kill $(jobs -pr)' INT QUIT TERM EXIT [...]
Neden? Çünkü sadece trap 'kill $(jobs -pr)' [...]birini kullanarak , tuzak koşulu bildirildiğinde arka plan işlerinin çalışacağını varsayar . Hiç iş olmadığında, aşağıdaki (veya benzeri) bir mesaj görüntülenir:
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
çünkü jobs -prboş - Ben bu 'tuzak' ile sona erdi (cinas amaçlanan).
[ -n "$(jobs -pr)" ]senaryosu bash üzerinde çalışmıyor. GNU bash, sürüm 4.2.46 (2) -release (x86_64-redhat-linux-gnu) kullanıyorum. "Kill: use" mesajı görüntülenmeye devam ediyor.
jobs -prArka plan süreçlerinin çocuklarının PID'lerini geri döndürmemesi gerçeğiyle ilgili olduğundan şüpheleniyorum . Tüm işlem ağacını yıkmaz, sadece kökleri keser.
Linux, BSD ve MacOS X altında çalışan güzel bir sürüm. İlk önce SIGTERM göndermeye çalışır ve başarılı olmazsa işlemi 10 saniye sonra öldürür.
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
İşlerin büyük çocuk süreçlerini içermediğini lütfen unutmayın.
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
Gibi https://stackoverflow.com/a/22644006/10082476 ancak katma exit-koduyla
exit_codegeliyor INT TERM?
jobs -p, bir alt kabukta çağrılırsa, tümünün çıktısı bir dosyaya yönlendirilmez, ancak bir boruya yönlendirilmezse, tüm kabuklarda çalışmaz. (Başlangıçta yalnızca etkileşimli kullanım için tasarlandığını varsayıyorum.)
Aşağıdakiler ne olacak:
trap 'while kill %% 2>/dev/null; do jobs > /dev/null; done' INT TERM EXIT [...]
"İşler" çağrısı Debian'ın mevcut işi ("%%") eksikse güncellemeyen çizgi kabuğunda gereklidir.
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; doneBash (5.0.3) 'te çalıştırırsanız ve sonlandırmaya çalışırsanız, sonsuz bir döngü varmış gibi görünür. Ancak, tekrar sonlandırırsanız çalışır. Dash (0.5.10.2-6) ile bile iki kez sonlandırmanız gerekir.
Bir ön plan işlemi çalıştırdığımda tetiklenmediğini fark ettiğimde , http://veithen.github.io/2014/11/16/sigterm-propagation.html bilgisiyle birleştirilen @ tokland'ın cevabına uyum sağladım. trap(arka planla belirtilmemiş &):
#!/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
Çalışma örneği:
$ 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
Sadece çeşitlilik için https://stackoverflow.com/a/2173421/102484 varyasyonunu göndereceğim , çünkü bu çözüm ortamımda "Sonlandırıldı" iletisine yol açıyor:
trap 'test -z "$intrap" && export intrap=1 && kill -- -$$' SIGINT SIGTERM EXIT