Невозможно перехватить внешний сигнал в bash - PullRequest
1 голос
/ 16 февраля 2020

Я пытаюсь написать скрипт, который в основном будет бездействовать в течение предопределенного периода времени, используя обычную систему sleep. Проблема возникает, когда я пытаюсь убить его извне (например, start-stop-daemon). Основной процесс будет убит, но дочерний сон будет оставаться в системе до тех пор, пока он не закончится. Я решил сделать ловушку здравомыслия и убить активную sleep из самого скрипта. Вот как это делается:

cleanup()
{
        local PIDS=$(jobs -p)
        echo $PIDS
        [ -n "$PIDS" ] && kill $PIDS
        exit 0
}
trap "cleanup" SIGINT SIGTERM

sleep 1h

Когда я нажимаю Ctrl- C (отправить SIGINT), когда сценарий находится на переднем плане, запускается процедура cleanup (), но если я пытаюсь убить (отправить по умолчанию SIGTERM) запущенный скрипт из другой консоли ничего не происходит. Нет очистки () не выполняется, ни один сценарий не завершается. Сценарий будет продолжать работать так же, как ничего не происходит. Кто-нибудь может объяснить, что происходит и как перехватить внешний SIGTERM и выполнить требуемую процедуру?

1 Ответ

3 голосов
/ 16 февраля 2020

Как Филипп упоминает в комментарии выше, Справочное руководство Bash § 3.7.6 "Сигналы" состояния в части:

Если Bash ожидает завершения команды и получает сигнал, для которого установлена ​​ловушка, ловушка не будет выполнена до ее завершения.

Это на самом деле верно как для SIGINT, так и для SIGTERM; причина, по которой ваш подход работает для Ctrl- C, заключается в том, что Ctrl- C отправляет SIGINT на каждый процесс в группе процессов переднего плана, включая процесс sleep, поэтому sleep завершается немедленно и затем его родительский скрипт завершается.

Наиболее явный способ исправить это предлагает остальная часть абзаца, который я только что процитировал:

Когда Bash ожидает асинхронная команда через встроенную функцию wait, прием сигнала, для которого установлена ​​ловушка, заставит встроенную функцию wait немедленно вернуться с состоянием выхода больше 128, сразу после чего ловушка будет выполнена.

Другими словами, вы можете заменить sleep 1h на sleep 1h & wait (где sleep 1h & и wait могут быть в отдельных строках или в одной строке, как вы предпочитаете), чтобы ваш trap вызывается немедленно (так что он может убить процесс sleep).

В качестве альтернативы, вы можете исключить установку trap и заменить sleep 1h чем-то, что не будет работать так долго после выхода из скрипта ; например:

  • al oop, который многократно спит ненадолго (например, sleep 1s), пока не пройдет час.
  • read -t 3600, который ожидает до 3600 секунд ( один час) для отображения текста на стандартном вводе. (Примечание: этот подход работает только в том случае, если на стандартном вводе фактически нет текста.)

(Они основаны на том факте, что l oop (в первом случае) или read вызов (в последнем случае) является частью самого процесса Bash, а не раздвоенным дочерним процессом.)

...