Arka plan işlemlerini Ctrl + C tuşlarına basarken bir kabuk betiğinde nasıl bitirmesini bekleyebilirim?


24

Bir kabuk betiği kurmaya çalışıyorum, böylece arkaplan işlemlerini yürütüyor ve ben Ctrlckabuk betiği, çocukları öldürüyor, sonra çıkıyor.

Gelebildiğim en iyisi bu. kill 0 -INTBekleme gerçekleşmeden önce betiği de öldürdüğü anlaşılıyor , bu nedenle kabuk betiği çocuklar tamamlanmadan ölüyor.

Bu kabuk betiğini gönderdikten sonra çocukların ölmesini bekleyebilmem konusunda herhangi bir fikrim var INTmı?

#!/bin/bash
trap 'killall' INT

killall() {
    echo "**** Shutting down... ****"
    kill 0 -INT
    wait # Why doesn't this wait??
    echo DONE
}

process1 &
process2 &
process3 &

cat # wait forever


"Shell intercept kill switch" i arayarak bu soruyu buldum, tuzak komutunu paylaştığınız için teşekkür ederiz, uygulama yakın GUI düğmesiyle doğru bir şekilde çıkmadığında, Ctrl + C ile bir uygulamadan çıktıktan sonra bir komutu çalıştırmak çok yararlı olur.
baptx

Yanıtlar:


22

Emriniz killgeriye doğru.

Birçok UNIX komutunda olduğu gibi, eksi ile başlayan seçeneklerin önce diğer argümanlardan önce gelmesi gerekir.

Eğer yazarsan

kill -INT 0

gördüğü -INTbir seçenek olarak ve gönderir SIGINTiçin 0( 0cari işlem grubundaki tüm süreçleri anlam özel bir sayı).

Ama yazarsan

kill 0 -INT

görür, 0başka seçenek kalmayacağına karar verir, bu nedenle SIGTERMvarsayılan olarak kullanır . Ve bunu , o anki süreç grubuna gönderir , yaptığınız gibi

kill -TERM 0 -INT    

(o da göndermeyi denemek istiyorum SIGTERMiçin -INTbir sözdizimi hatası neden olur ki, ama gönderir SIGTERMiçin 0ilk ve çok olduğunu geçmez.)

Demek ana senaryonuz ve ' SIGTERMyi çalıştırmadan önce oluyor .waitecho DONE

Eklemek

trap 'echo got SIGTERM' TERM

tepede, hemen sonra

trap 'killall' INT

ve bunu kanıtlamak için tekrar çalıştırın.

Stephane Chazelas'ın işaret ettiği gibi, arka plandaki çocuklarınız ( process1, vb.) SIGINTVarsayılan olarak görmezden gelecektir .

Her durumda, göndermenin SIGTERMdaha anlamlı olacağını düşünüyorum .

Son olarak, önce kill -process groupçocuklara gitme garantisi olup olmadığından emin değilim . Kapatma sırasında sinyalleri dikkate almamak iyi bir fikir olabilir.

Öyleyse şunu dene:

#!/bin/bash
trap 'killall' INT

killall() {
    trap '' INT TERM     # ignore INT and TERM while shutting down
    echo "**** Shutting down... ****"     # added double quotes
    kill -TERM 0         # fixed order, send TERM not INT
    wait
    echo DONE
}

./process1 &
./process2 &
./process3 &

cat # wait forever

Teşekkürler, bu işe yarıyor! Bu sorun olmuş gibi görünüyor.
slipheed

9

Ne yazık ki, arka planda başlatılan komutlar, SIGINT'i görmezden gelecek şekilde kabuk tarafından ayarlanır ve daha kötüsü, onu görmezden gelemezler trap. Aksi takdirde, tek yapmanız gereken şey

(trap - INT; exec process1) &
(trap - INT; exec process2) &
trap '' INT
wait

Çünkü C1T-C'ye bastığınızda işlem1 ve işlem2 SIGINT'i alır çünkü terminalin ön plan işlem grubu olan aynı işlem grubunun bir parçası olurlar.

Yukarıdaki kod, bu konuda POSIX'e uygun olmayan pdksh ve zsh ile çalışacaktır.

Diğer mermilerde, SIGINT gibi varsayılan işleyiciyi geri yüklemek için başka bir şey kullanmanız gerekir:

perl -e '$SIG{INT}=DEFAULT; exec "process1"' &

veya SIGTERM gibi farklı bir sinyal kullanın.


2

Sadece bir süreci öldürmek ve ölmesini beklemek isteyenler için , ancak süresiz olarak :

Her sinyal tipi için maksimum 60 saniye bekler.

Uyarı: Bu cevap, bir öldürme sinyalini yakalama ve gönderme ile ilgili değildir.

# close_app_sub GREP_STATEMENT SIGNAL DURATION_SEC
# GREP_STATEMENT must not match itself!
close_app_sub() {
    APP_PID=$(ps -x | grep "$1" | grep -oP '^\s*\K[0-9]+' --color=never)
    if [ ! -z "$APP_PID" ]; then
        echo "App is open. Trying to close app (SIGNAL $2). Max $3sec."
        kill $2 "$APP_PID"
        WAIT_LOOP=0
        while ps -p "$APP_PID" > /dev/null 2>&1; do
            sleep 1
            WAIT_LOOP=$((WAIT_LOOP+1))
            if [ "$WAIT_LOOP" = "$3" ]; then
                break
            fi
        done
    fi
    APP_PID=$(ps -x | grep "$1" | grep -oP '^\s*\K[0-9]+' --color=never)
    if [ -z "$APP_PID" ]; then return 0; else return "$APP_PID"; fi
}

close_app() {
    close_app_sub "$1" "-HUP" "60"
    close_app_sub "$1" "-TERM" "60"
    close_app_sub "$1" "-SIGINT" "60"
    close_app_sub "$1" "-KILL" "60"
    return $?
}

close_app "[f]irefox"

Adı veya argümanlarına göre öldürmek için uygulamayı seçer. Grep ile eşleşmekten kaçınmak için, uygulama adının ilk harfi için braketleri saklayın.

Bazı değişikliklerle, doğrudan PID'yi veya ifade pidof process_nameyerine daha basit bir yöntemi kullanabilirsiniz ps.

Kod detayları: Son adım PID'yi boşluk bırakmadan almaktır.


1

İstediğiniz bazı arka plan işlemlerini yönetmekse, neden bash iş kontrolü özelliklerini kullanmıyorsunuz?

$ gedit &
[1] 2581
$ emacs &
[2] 2594
$ jobs
[1]-  Running                 gedit &
[2]+  Running                 emacs &
$ jobs -p
2581
2594
$ kill -2 `jobs -p`
$ jobs
[1]-  Interrupt               gedit
[2]+  Done                    emacs

0

Ben de bununla dolaştım. Bash'de işlevleri tanımlayabilir ve bunlarla süslü şeyler yapabilirsiniz. İş arkadaşlarım ve ben kullanıyoruz terminator, bu nedenle toplu işlemler için normalde çıktıyı görüntülemek istiyorsak (bir tmuxsekmeden daha az zarif , ancak daha fazla GUI benzeri bir arabirim kullanabilirsiniz) bir dizi sonlandırma penceresi ortaya çıkardık.

Buraya geldiğim kurulumun yanı sıra çalıştırabileceğiniz şeylere bir örnek:

#!/bin/bash
custom()
{
    terun 'something cool' 'yes'
    run 'xclock'
    terun 'echoes' 'echo Hello; echo Goodbye; read'
    func()
    {
        local i=0
        while :
        do
            echo "Iter $i"
            let i+=1
            sleep 0.25
        done
    }
    export -f func
    terun 'custom func' 'func'
}

# [ Setup ]
run()
{
    "$@" &
    # Give process some time to start up so windows are sequential
    sleep 0.05
}
terun()
{
    run terminator -T "$1" -e "$2"
}
finish()
{
    procs="$(jobs -p)"
    echo "Kill: $procs"
    # Ignore process that are already dead
    kill $procs 2> /dev/null
}
trap 'finish' 2

custom

echo 'Press <Ctrl+C> to kill...'
wait

Çalıştırılıyor

$ ./program_spawner.sh 
Press <Ctrl+C> to kill...
^CKill:  9835
9840
9844
9850
$ 

Düzen @Maxim, sadece bir ton basit hale getireceğini, önerinizi gördüm! Teşekkürler!

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.