Ловушка Баш не убивает детей, вызывает неожиданное поведение ctrl-c
редактировать
Для будущих читателей. Корень этой проблемы на самом деле сводится к запуску функции в интерактивной оболочке, а не к отдельному сценарию.
Кроме того, есть много вещей, которые можно улучшить в коде, который я первоначально разместил. Пожалуйста, смотрите комментарии для вещей, которые могли / должны были быть сделаны лучше.
/редактировать
У меня есть функция bash, предназначенная для повторного запуска процесса в фоновом режиме при изменении файлов в каталоге (например, Grunt, но для общих целей). Скрипт работает по желанию во время работы:
- Подпроцесс правильно запущен (включая любые дочерние элементы)
- При изменении файла саб убивается (включая детей) и запускается снова
Однако при выходе (ctrl-c) ни один из процессов не завершается. Кроме того, повторное нажатие ctrl-c завершит текущий сеанс терминала. Я предполагаю, что это проблема с моей ловушкой, но я не смог определить причину проблемы.
Вот код rerun.sh
#!/bin/bash
# rerun.sh
_kill_children() {
isTop=$1
curPid=$2
# Get pids of children
children=`ps -o pid --no-headers --ppid ${curPid}`
for child in $children
do
# Call this function to get grandchildren as well
_kill_children 0 $child
done
# Parent calls this with 1, all other with 0 so only children are killed
if [[ $isTop -eq 0 ]]; then
kill -9 $curPid 2> /dev/null
fi
}
rerun() {
trap " _kill_children 1 $$; exit 0" SIGINT SIGTERM
FORMAT=$(echo -e "\033[1;33m%w%f\033[0m written")
#Command that should be repeatedly run is passed as args
args=$@
$args &
#When a file changes in the directory, rerun the process
while inotifywait -qre close_write --format "$FORMAT" .
do
#Kill current bg proc and it's children
_kill_children 1 $$
$args & #Rerun the proc
done
}
#This is sourced in my bash profile so I can run it any time
Чтобы проверить это, создайте пару исполняемых файлов parent.sh и child.sh следующим образом:
#!/bin/bash
#parent.sh
./child.sh
#!/bin/bash
#child.sh
sleep 86400
Затем создайте файл rerun.sh и запустите rerun ./parent.sh
, В другом окне терминала я watch "ps -ef | grep pts/4"
чтобы увидеть все процессы для повторного запуска (в этом примере на pts/4). Касание файла в каталоге вызывает перезапуск parent.sh и дочерних элементов. [ctrl-c] выходит, но запускает пиды. [ctrl-c] снова убивает bash и все остальные процессы в pts/4.
Желаемое поведение: на [ctrl-c] убить детей и нормально выйти из оболочки. Помогите?
- Исходники кода:
Inotify идея от: https://exyr.org/2011/inotify-run/
Убить детей из: http://riccomini.name/posts/linux/2012-09-25-kill-subprocesses-linux-bash/
1 ответ
Это не хорошая практика, чтобы следовать в первую очередь. Отследите своих детей явно:
children=( )
foo & children+=( "$!" )
... затем вы можете явно убить или дождаться их, ссылаясь на "${children[@]}"
для списка. Если вы хотите получить внуков, это хороший пользователь для fuser -k
и файл блокировки:
lockfile_name="$(mktemp /tmp/lockfile.XXXXXX)" # change appropriately
trap 'rm -f "$lockfile_name"' 0
exec 3>"$lockfile_name" # open lockfile on FD 3
kill_children() {
# close our own handle on the lockfile
exec 3>&-
# kill everything that still has it open (our children and their children)
fuser -k "$lockfile_name" >/dev/null
# ...then open it again.
exec 3>"$lockfile_name"
}
rerun() {
trap 'kill_children; exit 0' SIGINT SIGTERM
printf -v format '%b' "\033[1;33m%w%f\033[0m written"
"$@" &
#When a file changes in the directory, rerun the process
while inotifywait -qre close_write --format "$format" .; do
kill_children
"$@" &
done
}