Я ищу способ навести порядок, когда выйдет мой скрипт верхнего уровня.
Особенно, если я хочу использовать set -e
, я хотел бы, чтобы фоновый процесс умер при выходе из скрипта.
Чтобы навести порядок, trap
можно использовать. Он может предоставить список вещей, выполняемых при поступлении определенного сигнала:
trap "echo hello" SIGINT
но также может использоваться для выполнения чего-либо, если оболочка завершает работу:
trap "killall background" EXIT
Это встроенный, поэтому help trap
даст вам информацию (работает с Bash). Если вы хотите убить только фоновые задания, вы можете сделать
trap 'kill $(jobs -p)' EXIT
Остерегайтесь использовать одиночный '
, чтобы предотвратить $()
немедленную замену оболочки .
kill $(jobs -p)
не работает в dash, потому что выполняет подстановку команд в подоболочке (см. Подстановка команд в man dash)killall background
должно быть заполнителем?background
нет вЭто работает для меня (улучшилось благодаря комментаторам):
kill -- -$$
отправляет SIGTERM всей группе процессов, таким образом убивая также потомков.Указание сигнала
EXIT
полезно при использованииset -e
(подробнее здесь ).источник
4.3.30(1)-release
OSX, и это также подтверждается на Ubuntu . Впрочем , есть и обвоиусный оборот :)-$$
. Он оценивается как '- <PID> `например-1234
. На встроенной man-странице kill // главная черточка указывает сигнал для отправки. Однако - возможно, это блокирует, но в противном случае начальная черта не документирована. Любая помощь?man 2 kill
, которая объясняет, что когда PID является отрицательным, сигнал отправляется всем процессам в группе процессов с предоставленным идентификатором ( en.wikipedia.org/wiki/Process_group ). Это сбивает с толку, что это не упоминается вman 1 kill
илиman bash
, и может рассматриваться как ошибка в документации.Обновление: https://stackoverflow.com/a/53714583/302079 улучшает это, добавляя статус выхода и функцию очистки.
Зачем конвертировать
INT
иTERM
выходить? Потому что оба должны вызыватьkill 0
без входа в бесконечный цикл.Почему триггер
kill 0
наEXIT
? Потому что нормальные выходы скриптаkill 0
тоже должны срабатывать .Почему
kill 0
? Потому что вложенные вложенные оболочки также должны быть убиты. Это уничтожит все дерево процессов .источник
kill 0
означает / делает?Я бы внес лишь незначительные изменения в ответ Йоханнеса и использовал бы задания -pr, чтобы ограничить уничтожение запущенными процессами и добавить еще несколько сигналов в список:
источник
trap 'kill 0' SIGINT SIGTERM EXIT
Решение , описанное в @ tokland отвечают очень приятно, но последняя Bash падает с ошибкой segmantation при ее использовании. Это потому, что Bash, начиная с v. 4.3, допускает рекурсию ловушек, которая становится бесконечной в этом случае:SIGINT
илиSIGTERM
илиEXIT
;kill 0
, что отправляетSIGTERM
всем процессам в группе, включая саму оболочку;Это можно обойти, вручную отменив регистрацию ловушки:
Более причудливый способ, который позволяет печатать полученный сигнал и избегает сообщений "Termination:":
UPD : добавлен минимальный пример; улучшена
stop
функция, позволяющая избежать захвата ненужных сигналов и скрыть сообщения «Ter прекращено:» с выхода. Спасибо Тревору Бойду Смиту за предложения!источник
stop()
вас предоставить первый аргумент как номер сигнала , но тогда вы жёстко , что сигналы были сняты с регистрации. вместо жесткого кодирования отменяемых сигналов вы можете использовать первый аргумент для отмены регистрации вstop()
функции (это может привести к остановке других рекурсивных сигналов (кроме трех жестко закодированных)).SIGINT
, ноkill 0
отправляетSIGTERM
, что снова попадет в ловушку. Это не приведет к бесконечной рекурсии, потому чтоSIGTERM
будет захвачено во время второгоstop
вызова.trap - $1 && kill -s $1 0
должно работать лучше. Я проверю и обновлю этот ответ. Спасибо за хорошую идею! :)trap - $1 && kill -s $1 0
тоже не получится, потому что мы не можем убитьEXIT
. Но действительно достаточно сделать de-trapTERM
, потому чтоkill
посылает этот сигнал по умолчанию.EXIT
,trap
обработчик сигнала всегда выполняется только один раз.Чтобы быть в безопасности, я считаю, что лучше определить функцию очистки и вызвать ее из ловушки:
или вообще избегать функции:
Зачем? Потому что простое использование
trap 'kill $(jobs -pr)' [...]
предполагает, что будут выполняться фоновые задания, когда сигнализируется условие прерывания. Когда нет рабочих мест, вы увидите следующее (или похожее) сообщение:потому что
jobs -pr
пусто - я закончил в этой «ловушке» (каламбур).источник
[ -n "$(jobs -pr)" ]
не работает на моем Bash. Я использую GNU bash, версия 4.2.46 (2) -релиз (x86_64-redhat-linux-gnu). Сообщение «kill: использование» продолжает появляться.jobs -pr
не возвращает PID дочерних элементов фоновых процессов. Он не разрушает все дерево процессов, а только обрезает корни.Хорошая версия, которая работает под Linux, BSD и MacOS X. Сначала пытается отправить SIGTERM, и, если это не удается, убивает процесс через 10 секунд.
Обратите внимание, что работа не включает в себя процессы внучат
источник
Как https://stackoverflow.com/a/22644006/10082476 , но с добавленным кодом выхода
источник
exit_code
приходят из вINT TERM
ловушку?Другой вариант - установить сценарий в качестве лидера группы процессов и перехватить killpg в вашей группе процессов при выходе.
источник
Так что скрипт загрузки скрипта. Запустите
killall
(или все, что доступно в вашей ОС) команду, которая выполняется, как только сценарий будет завершен.источник
jobs -p не работает во всех оболочках, если вызывается в под-оболочке, возможно, если его вывод не перенаправлен в файл, но не в канал. (Я предполагаю, что первоначально он был предназначен только для интерактивного использования.)
Как насчет следующего:
Вызов «jobs» необходим в dash-оболочке Debian, которая не может обновить текущее задание («%%»), если оно отсутствует.
источник
trap 'echo in trap; set -x; trap - TERM EXIT; while kill %% 2>/dev/null; do jobs > /dev/null; done; set +x' INT TERM EXIT; sleep 100 & while true; do printf .; sleep 1; done
Если вы запустите его в Bash (5.0.3) и попытаетесь завершиться, похоже, существует бесконечный цикл. Однако, если вы прекратите его снова, это работает. Даже с помощью Dash (0.5.10.2-6) вы должны завершить его дважды.Я адаптировал ответ @ tokland в сочетании со знаниями из http://veithen.github.io/2014/11/16/sigterm-propagation.html, когда заметил, что
trap
это не сработает, если я запускаю процесс переднего плана (без фона&
):Пример того, как это работает:
источник
Просто для разнообразия я опубликую вариант https://stackoverflow.com/a/2173421/102484 , потому что это решение приводит к сообщению «Прекращено» в моей среде:
источник