Перехватите EXIT и убейте с помощью управления заданиями bash

Перехватите EXIT и убейте с помощью управления заданиями bash

У меня есть скрипт bash, который запускает несколько программ.

#!/bin/sh
python program1.py &
python program2.py &
other programs ... &
lastProgram

Я управляю им как./myscript.sh

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

Как правильно закрыть все программы, запущенные из скрипта?

решение1

Соберите идентификаторы процессов, завершите фоновые процессы при выходе.

#!/bin/bash
killbg() {
        for p in "${pids[@]}" ; do
                kill "$p";
        done
}
trap killbg EXIT
pids=()
background job 1 & 
pids+=($!)
background job 2... & 
pids+=($!)
foreground job

Trapping EXITзапускает функцию при выходе из оболочки, независимо от причины. Вы можете изменить это на trap killbg SIGINTзапуск только на ^C.

Это не проверяет, завершился ли один из фоновых процессов, прежде чем скрипт попытается их застрелить. Если они это сделают, вы можете получить ошибки или, что еще хуже, застрелить не тот процесс.


Или убейте их по идентификатору задания. Давайте прочитаем вывод, jobsчтобы узнать, какие из них все еще активны.

#!/bin/bash 
killjobs() {
    for x in $(jobs | awk -F '[][]' '{print $2}' ) ; do 
        kill %$x
    done
}
trap killjobs EXIT
sleep 999 &
sleep 1 &
sleep 999 &
sleep 30

Если вы запускаете фоновые процессы, которые порождают другие процессы (например, подоболочку: (sleep 1234 ; echo foo) &), вам нужно включить управление заданиями с помощью set -m("режим монитора"), чтобы это работало. В противном случае завершается только ведущий процесс.

решение2

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

Перехватите EXIT и убейте с помощью управления заданиями bash

Вы можете использовать управление заданиями bash, чтобы завершать только процессы, запущенные в скрипте с помощью ловушки и %n jobspec, подсчитывая максимальное количество заданий, которые могут быть запущены (в этом примере только 3):

#!/bin/bash
#trap 'kill %1 %2 %3' 0     # 0 or EXIT are equivalent
#trap 'kill %1 %2 %3' EXIT  # or use {1..n} as below
trap 'kill %{1..3}' EXIT
sleep 33 &
sleep 33 &
sleep 33 &
echo processes are running, ctrl-c the next sleep
sleep 66
echo this line will never be executed

Любые дополнительные «завершения» несуществующих спецификаций заданий, которые уже завершены, приведут только к сообщению об ошибке, но не завершат никакие другие новые/случайные процессы.


убить всю группу процессов вашего скрипта

Вот немного другой способ убить всю группу процессов вашего скрипта. Но если управление заданиями вашего скрипта/оболочки не настроено, то этомогунаследовать свой PPID от родителя... но без управления заданиями вышесказанное тоже не будет работать.

Разница в том, что эта команда kill для trap использует PID bash, поскольку он становится PGID для новых процессов:

trap 'kill -- -$$' EXIT

Видетьэтот связанный Qилиздесь Йоханнес «рыба» Цимке ловит SIGINT и SIGTERM и использует их setsidдля «убить всю группу процессов в новой группе процессов, чтобы не рисковать убить себя.")

решение3

Если вы хотите завершить все фоновые процессы, если они не завершатся до lastProgramокончания выполнения, вам нужно будет сохранить все pid:

python program1.py &
p1pid=$!
python program2.py &
p2pid=$!
other programs ... &
p3pid=$!
lastProgram

kill $p1pid $p2pid $p3pid

Если вы просто хотите дождаться завершения выполнения всех фоновых процессов, прежде чем выйти из скрипта, вы можете использовать команду wait.

python program1.py &
python program2.py &
other programs ... &
lastProgram
wait

решение4

Вот моя однострочная команда (на bash):

trap "jobs -p | xargs kill ; trap - INT" INT ; cmd1 & cmd2 & cmd3 &
# replace `cmds` with your command

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

Затем он запускает все команды в фоновом режиме.

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

Связанный контент