Kabuk betiğimden çıktığımda arka plan işlemlerini / işleri nasıl öldürebilirim?


194

Üst düzey komut dosyam çıktığında karışıklığı temizlemenin bir yolunu arıyorum.

Özellikle kullanmak set -eistersem, senaryo çıktığında arka plan işleminin ölmesini diliyorum.

Yanıtlar:


186

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.


o zaman sadece çocuğu nasıl öldürürsün ? (veya bariz bir şey mi kaçırıyorum)
elmarco

18
killall çocuklarınızı öldürüyor, ama siz değil
Aralık'ta

4
kill $(jobs -p)alt kabukta komut ikamesi yürüttüğü için tire içinde çalışmaz (bkz. man
dash'de

8
olan killall backgroundbir yer tutucu olması gerekiyordu? backgroundadam sayfasında değil ...
Evan Benn

170

Bu benim için çalışıyor (yorumcular sayesinde geliştirildi):

trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
  • kill -- -$$tüm süreç grubuna bir SIGTERM gönderir , böylece torunları da öldürür.

  • EXITKullanırken sinyal belirtmek yararlıdır set -e(daha fazla ayrıntı burada ).


1
Genel olarak iyi çalışmalıdır, ancak alt süreçler süreç gruplarını değiştirebilir. Öte yandan, iş kontrolü gerektirmez ve diğer çözümler tarafından kaçırılan bazı torun süreçlerini de alabilir.
michaeljt

5
"Kill 0" da bir ana bash betiğini öldürecektir. Yalnızca geçerli komut dosyasının alt öğelerini öldürmek için "kill - - $ BASHPID" işlevini kullanmak isteyebilirsiniz. Bash sürümünüzde $ BASHPID yoksa, BASHPID = $ (sh -c 'echo $ PPID')
ACyclic

2
Güzel ve net çözüm için teşekkürler! Ne yazık ki, tuzak özyinelemesine izin veren Bash 4.3'ü segmente eder. Bunu 4.3.30(1)-releaseOSX'te çalıştırdım ve Ubuntu'da da onaylandı . Yine de bir obvoius wokaround var :)
skozin

4
Tam olarak anlamıyorum -$$. '- <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?
Evan Benn

4
@EvanBenn: 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 .
user001

111

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 .


3
Debian davam için tek çözüm.
MindlessRanger

3
Ne Johannes Schaub'un cevabı ne de tokland'ın verdiği cevap, kabuk senaryomun başladığı arka plan süreçlerini (Debian'da) öldürmeyi başaramadı. Bu çözüm işe yaradı. Bu cevabın neden daha iyi değerlendirilmediğini bilmiyorum. Tam olarak ne kill 0anlama geldiği / ne anlama geldiği hakkında daha fazla bilgi verebilir misiniz ?
josch

7
Bu harika, ama aynı zamanda ana kabuğumu da öldürüyor :-(
vidstige

5
Bu çözüm tam anlamıyla aşırıdır. öldürmek 0 (benim senaryom içinde) benim bütün X oturumu mahvetti! Belki de bazı durumlarda 0 öldürmek yararlı olabilir, ancak bu, genel çözüm olmadığı gerçeğini değiştirmez ve kullanmak için çok iyi bir neden olmadığı sürece mümkünse kaçınılmalıdır. Bir komut dosyasının arka plan işlerini değil, ana kabuğu veya hatta tüm X oturumunu öldürebileceğine dair bir uyarı eklemek güzel olurdu!
Lissanro Rayen

3
Bu, bazı durumlarda ilginç bir çözüm olsa da, @vidstige tarafından belirtildiği gibi, bu , başlatma işlemini içeren tüm süreç grubunu (yani çoğu durumda ana kabuk) öldürecektir . Bir IDE üzerinden bir komut dosyası çalıştırırken kesinlikle istediğiniz bir şey değildir.
18'de

21

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

Neden durdurulan işleri de öldürmüyorsun? Bash'de EXIT tuzağı, SIGINT ve SIGTERM durumunda da çalıştırılacaktır, bu nedenle böyle bir sinyal durumunda tuzak iki kez çağrılacaktır.
jarno

14

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:

  1. kabuk işlemi recieves SIGINTveya SIGTERMveya EXIT;
  2. sinyal , kabuğun kendisi de dahil olmak üzere gruptaki tüm süreçlere kill 0gönderen, yakalanır SIGTERM;
  3. 1'e git :)

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 !


içinde 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 .
Trevor Boyd Smith

@TrevorBoydSmith, bu beklendiği gibi çalışmaz, sanırım. Örneğin, kabuk ile öldürülebilir 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 .
skozin

Muhtemelen 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! :)
skozin

Hayır, 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.
skozin

İle özyinelemeyi test ettim EXIT, trapsinyal işleyici her zaman sadece bir kez yürütülür.
Trevor Boyd Smith

9

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).


Bu test [ -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.
Douwe van der Leest

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.
Douwe van der Leest

2

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.



1

Başka bir seçenek de komut dosyasının işlem grubu lideri olarak ayarlanması ve çıkışta işlem grubunuzda bir killpg yakalamasıdır.


Süreci süreç grubu lideri olarak nasıl ayarlarsınız? "Killpg" nedir?
jarno

0

Yani betiğin yüklenmesi betiği. killallKomut dosyası tamamlanır tamamlanmaz çalışan bir (veya işletim sisteminizde mevcut olan) komutu çalıştırın.


0

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.


Hmm ilginç bir yaklaşım, ama işe yaramıyor gibi görünüyor. Script'i düşünün 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.
jarno

0

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.