Сценарий оболочки: как предотвратить прерывание SIGINT текущей задачи - PullRequest
0 голосов
/ 27 мая 2018

У меня довольно длинный сценарий оболочки, и я пытаюсь добавить к нему обработку сигналов.

Основная задача сценария - запускать различные программы, а затем очищать их временные файлы.

Я хочу поймать SIGINT.Когда сигнал пойман, сценарий должен дождаться завершения выполнения текущей программы, затем выполнить очистку и выйти.

Вот MCVE:

#!/bin/sh

stop_this=0
trap 'stop_this=1' 2

while true ; do
    result="$(sleep 2 ; echo success)" # run some program
    echo "result: '$result'"
    echo "Cleaning up..." # clean up temporary files
    if [ $stop_this -ne 0 ] ; then
        echo 'OK, time to stop this.'
        break
    fi
done

exit 0

Ожидаемый результат:

Cleaning up...
result: 'success'
Cleaning up...
^Cresult: 'success'
Cleaning up...
OK, time to stop this.

Фактический результат:

Cleaning up...
result: 'success'
Cleaning up...
^Cresult: ''
Cleaning up...
OK, time to stop this.

Проблема в том, что текущая выполняемая инструкция (в данном случае result="$(sleep 2 ; echo success)") прерывается.Что я могу сделать, чтобы он вел себя больше, как если бы я был установлен trap '' 2?

Я ищу либо решение POSIX, либо решение, которое поддерживается большинством интерпретаторов оболочки (BusyBox, dash, Cygwin ...)

Я уже видел ответы для Запретить SIGINT закрывать дочерний процесс в скрипте bash , но на самом деле это не работает для меня.Все эти решения требуют модификации каждой строки, которая не должна прерываться.Мой настоящий сценарий довольно длинный и намного сложнее, чем пример.Мне бы пришлось изменить сотни строк.

Ответы [ 3 ]

0 голосов
/ 27 мая 2018

Sighandling в сценариях оболочки может стать неуклюжим.Практически невозможно сделать это «правильно» без поддержки C.

Проблема с:

result="$(sleep 2 ; echo success)" # run some program

состоит в том, что $() создает подоболочку и в подоболочках не игнорируется(trap '' SIGNAL - это то, как вы игнорируете SIGNAL) сигналы сбрасываются в их расположение по умолчанию, которое для SIGINT означает прекращение процесса ($( ) получает свой собственный процесс, хотя он также получит сигнал, потому что сгенерированный терминалом SIGINTцелевая группа процессов)

Чтобы предотвратить это, вы можете сделать что-то вроде:

result="$(
trap '' INT #ignore; could get killed right before the trap command
sleep 2; echo success)"

или

result="$( trap : INT; #no-op handler; same problem
sleep 2; while ! echo success; do :; done)" 

, но, как уже было отмечено, будет небольшая гонка- окно условия между началом субоболочки и регистрацией обработчика сигнала, во время которого субоболочка может быть уничтожена сигналом SIGINT сброса к стандартному значению.

0 голосов
/ 28 мая 2018

Оба ответа @PSkocik и @WilliamPursell помогли мне встать на правильный путь.

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

#!/bin/sh

touch ./continue
trap 'rm -f ./continue' 2

( # the whole main body of the script is in a separate background process
trap '' 2 # ignore SIGINT
while true ; do
    result="$(sleep 2 ; echo success)" # run some program
    echo "result: '$result'"
    echo "Cleaning up..." # clean up temporary files
    if [ ! -e ./continue ] ; then # exit the loop if file "./continue" is deleted
        echo 'OK, time to stop this.'
        break
    fi
done
) & # end of the main body of the script
while ! wait ; do : ; done # wait for the background process to end (ignore signals)
wait $! # wait again to get the exit code
result=$? # exit code of the background process

rm -f ./continue # clean up if the background process ended without a signal

exit $result

РЕДАКТИРОВАТЬ :Есть некоторые проблемы с этим кодом в Cygwin.

Основные функции, касающиеся работы сигналов.Тем не менее, похоже, что законченный фоновый процесс не остается в системе как зомби.Это заставляет wait $! не работать.Код выхода скрипта имеет неверное значение 127.

Решением этой проблемы будет удаление строк wait $!, result=$? и result=$?, поэтому скрипт всегда возвращает 0. Это также должно быть возможносохранить правильный код ошибки, используя другой слой subshell, и временно сохранить код выхода в файле.

0 голосов
/ 27 мая 2018

Прежде всего, вы должны запретить SIGINT переходить к эху (или переписать cmd, который вы используете в назначении переменной, чтобы игнорировать SIGINT).Кроме того, необходимо разрешить присвоение переменной, и похоже, что оболочка прерывает назначение, когда получает SIGINT.Если вы беспокоитесь только о сгенерированном пользователем SIGINT из tty, вам нужно отсоединить эту команду от tty (например, вывести ее из группы процессов переднего плана) и запретить SIGINT прерывать назначение.Вы можете (почти) выполнить оба этих действия с помощью:

#!/bin/sh

stop_this=0

while true ; do
    trap 'stop_this=1' INT
    { sleep 1; echo success > tmpfile; } & # run some program
    while ! wait; do : ; done
    trap : INT
    result=$(cat tmpfile& wait)
    echo "result: '$result'"
    echo "Cleaning up..." # clean up temporary files
    if [ $stop_this -ne 0 ] ; then
        echo 'OK, time to stop this.'
        break
    fi
done

exit 0

Если вы беспокоитесь о SIGINT из другого источника, вам придется повторно реализовать sleep (или любую другую команду, которую я предполагаю * 1005).* является прокси для) для обработки SIGINT так, как вы хотите.Ключевым моментом здесь является запуск команды в фоновом режиме и ожидание, пока SIGINT не перейдет к ней и не завершит ее досрочно.Обратите внимание, что мы открыли здесь как минимум 2 новые банки с червями.Ожидая в цикле, мы фактически игнорируем любые ошибки, которые могут возникнуть подкомандой (мы делаем это, чтобы попытаться реализовать SIGRESTART), поэтому потенциально можем зависнуть.Кроме того, если SIGINT прибывает во время cat, мы попытались предотвратить прерывание cat, запустив его в фоновом режиме, но теперь присвоение переменной будет прервано, и вы получите исходное поведение.Обработка сигналов не является чистой в оболочке!Но это приближает вас к желаемой цели.

...