Назад | Перейти на главную страницу

Как я могу убить и дождаться завершения фоновых процессов в сценарии оболочки, когда я нажимаю Ctrl + C?

Я пытаюсь настроить сценарий оболочки так, чтобы он запускал фоновые процессы, и когда я нажимаю Ctrl + C сценарий оболочки, он убивает детей, а затем завершает работу.

Лучшее, что мне удалось придумать, это вот это. Кажется, что kill 0 -INT также убивает сценарий до того, как произойдет ожидание, поэтому сценарий оболочки умирает до завершения дочерних процессов.

Есть идеи, как заставить этот сценарий оболочки ждать, пока дети умрут после отправки INT?

#!/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

Во-первых, вы должны знать, что kill 0 отправить сигнал всем процессам в текущей группе процессов. (видеть kill (1) - справочная страница Linux) Полагаю, что убивает больше, чем следовало бы. Вот почему wait не ждет, он по какой-то причине прекращается. Лучше всего для решения этой проблемы перебрать выполняемые задания из jobs -pr убийство по одному: таким образом я гарантирую, что в этот момент сигнал будет отправлен только дочерним процессам и ничего более.

Чтобы заставить это работать, я провел несколько тестов, но застрял на отправке SIGINT дочерним процессам. Они просто на это не реагируют! Поискав в Интернете, я нашел этот ответ на Stack Overflow:

https://stackoverflow.com/questions/2524937/how-to-send-a-signal-sigint-from-script-to-script-bash

Итак, настоящая проблема в том, что вы не можете отправить SIGINT из одного скрипта в другой, потому что в неинтерактивных оболочках сигнал игнорируется. Мне не удалось обойти эту проблему (используя bash -i вызвать дочерний скрипт не получится).

Я знаю, что вы, вероятно, захотите отправить SIGINT и подождать, пока дочерние процессы корректно завершатся, но если вы не против использовать SIGTERM (или любой другой сигнал, кроме SIGINT), это лучший сценарий, который я написал:

#!/bin/bash
trap 'killall' INT

killall() {
    echo '**** Shutting down... ****'
    jobs -pr
    for i in $(jobs -pr); do
        kill -TERM $i
    done
    wait
    echo DONE
}

./long.sh &
./long.sh &
./long.sh &

cat # wait forever

Для проверки я создал это long.sh сценарий:

#!/bin/bash
trap 'killall' TERM

echo "Started... $$" >> file.txt

killall() {
    # shutting down gracefully
    echo "Finished... $$" >> file.txt
    exit # don't forget this!
}

# infinite loop
while true; do echo loop >/dev/null ; done

В последнем скрипте я не использовал sleep функция, потому что она создает новый процесс, который остается в памяти даже после завершения сценария. В вашем сценарии вы можете вызывать новые процессы, но вы должны обеспечить широковещательную передачу SIGTERM (или любого другого сигнала, который вы использовали) всем из них.

Кажется, это работает. Обязательно используйте "/ usr / bin / kill", а не встроенное в Bash "kill".

[myles@marklar ~]$ /usr/bin/kill --version
kill from util-linux-2.13-pre7

Обратите внимание, что управление заданиями не включено в неинтерактивных оболочках Bash (например, скриптах).

#!/bin/bash

trap kill_jobs 2

kill_jobs()
{
        while /usr/bin/kill 0; do :; done
        exit 0
}

foo &
foo &
foo &

sleep 777777