Вопрос или проблема
ArchLinux (Manjaro).
Я запускаю один bash файл. Он запускает 2 процесса (команды), используя &
. Но когда я нажимаю Ctrl+C, чтобы остановить программу – один процесс умирает, а другой продолжает работать.
Как мне остановить оба процесса? Или как мне написать новый скрипт для завершения этих двух процессов?
Обновление: trap
требует удаления префикса SIG
в условии, хотя некоторые оболочки поддерживают его использование. См. комментарий ниже.
Амперсанд “&” запускает команду в фоновом режиме в новом процессе. Когда ее родительский процесс (команда, запускающая bash скрипт в вашем случае) завершается, этот фоновый процесс сбрасывает своего родителя на init (процесс с PID
1), но не умирает. Когда вы нажимаете ctrl+c
, вы отправляете сигнал прерывания процессу на переднем плане, и это не повлияет на фоновый процесс.
Чтобы завершить фоновый процесс, вы должны использовать команду kill
с PID
самого последнего фонового процесса, который можно получить с помощью $!
.
Если вы хотите использовать ctrl+c
для завершения как скрипта, так и фонового процесса, вы можете сделать следующее:
trap 'kill $BGPID; exit' INT
sleep 1024 & # фоновая команда
BGPID=$!
sleep 1024 # команда скрипта на переднем плане
trap
изменяет обработчик сигнала SIGINT
( trap
требует удаления префикса SIG
, но некоторые оболочки могут поддерживать его использование), поэтому скрипт завершает процесс с $BGPID
перед выходом.
Программы могут игнорировать сигнал Ctrl+c
, так же как они могут игнорировать SIGTSTP.
Вы можете попробовать Ctrl+z
в большинстве оболочек (не всегда, но чаще всего)
Существуют некоторые сигналы, которые не могут быть проигнорированы процессом: SIGKILL, SIGSTOP. Вы можете отправить команду kill. Чтобы убить ваш зависший процесс, вам нужно найти идентификатор процесса (PID).
используйте pgrep или ps, а затем убейте его
% kill -9 PID
Для нескольких фоновых процессов
#! /bin/bash -e
# Убедитесь, что вы `kill` каждый из этих процессов по отдельности,
# если остановите выполнение на полпути, так как они просто продолжат работать в противном случае
trap 'for pid in ${main_pids[*]}; do kill $pid; done; exit' INT
main_pids=()
for i in "${!STATES_SETS[@]}"; do
time python script.py &
main_pids[${i}]=$!
done
# Ждем завершения всех pids, чтобы убедиться, что все в порядке
for pid in ${main_pids[*]}; do
wait $pid
done
В VSCode, похоже, если вы пытаетесь просмотреть идентификатор процесса, он дает вам какой-то странный идентификатор, который не совпадает с тем, что вы можете увидеть с помощью ps
. Тем не менее, код будет работать. Можно проверить с помощью: ps -aux | grep script.py
Ответ или решение
Чтобы эффективно завершить все фоновые процессы, запущенные в Bash-скрипте с использованием комбинации клавиш Ctrl+C
, вам необходимо правильно обработать сигнал прерывания (SIGINT) с помощью команды trap
. Ниже приведены шаги и пример скрипта, который демонстрирует, как это сделать.
Основная проблема
Когда вы запускаете команду в фоновом режиме с помощью символа &
, она работает в отдельном процессе. Когда вы нажимаете Ctrl+C
, прерывается только главный процесс скрипта, и фоновые процессы продолжают выполнение. Чтобы завершить оба, вам нужно предписать скрипту отлавливать сигнал прерывания и завершать все созданные фоновые процессы.
Пример скрипта
#! /bin/bash -e
# Инициализируем массив для хранения PID фоновых процессов
declare -a main_pids
# Устанавливаем обработчик сигнала для SIGINT
trap 'for pid in "${main_pids[@]}"; do kill $pid; done; exit' INT
# Запускаем фоновые процессы и сохраняем их PID
for i in {1..2}; do
sleep 60 & # Замените это на ваш команду
main_pids[$i]=$!
done
# Ожидаем завершения всех фоновых процессов
for pid in "${main_pids[@]}"; do
wait $pid
done
Объяснение кода
-
Объявление массива
main_pids
: Этот массив будет использоваться для хранения идентификаторов процессов (PID) фоновых задач. -
Обработчик сигнала
trap
: С помощью командыtrap
мы указываем, что при получении сигнала SIGINT (Ctrl+C
) необходимо выполнить цикл, который завершает каждый PID из массиваmain_pids
, после чего происходит выход из скрипта. -
Запуск фоновых процессов: В данном примере мы используем
sleep 60 &
как запущенную команду. Вы можете заменитьsleep 60
на любой другой процесс, который хотите запустить в фоновом режиме. -
Ожидание завершения процессов: Включает цикл
wait
, который будет дожидаться завершения всех фоновых процессов, используя их BGPID.
Примечания
- Сигнал SIGINT может быть игнорирован некоторыми программами. Если это произойдет, вы можете использовать более агрессивные методы завершения, такие как
kill -9 PID
, но будьте осторожны с этой командой, так как она может принудительно завершить процессы, не давая им возможности корректно завершиться. - Для завершения нескольких фоновых процессов можно использовать массив, как показано в примере, чтобы управлять их PID централизованно.
Следуя приведенной инструкции, вы сможете успешно завершить все фоновые процессы, запущенные в вашем Bash-скрипте, при нажатии комбинации клавиш Ctrl+C
.