подтвержденный выход с использованием ловушки

9

Я пытаюсь перехватить Ctrl+Cсигнал, запрашивая подтверждение у пользователя. Ловушка работает нормально. Но как только сигнал попадает в ловушку, он не возвращается к нормальному выполнению. Вместо этого он выходит из сценария. Как заставить его возобновить выполнение, когда пользователь нажимает нет.

вот мой код

hell()
{
echo "Do you want to quit? Press 1 for yes and 0 for no";
read n;
if [ $n == 1 ]; then
exit 1;
fi
}

trap "hell" SIGINT

find /
CHID
источник

Ответы:

12

Что происходит

Когда вы нажимаете Ctrl+ C, SIGINTсигнал доставляется всей группе процессов переднего плана . Здесь он отправляется как findпроцессу, так и вызывающему процессу оболочки. findреагирует, выходя немедленно, и оболочка реагирует, вызывая ловушку.

Если код в ловушке возвращается (т.е. не вызывает exit), выполнение продолжается с командой после той, которая была прервана сигналом. Здесь после findкоманды наступает конец сценария, поэтому сценарий завершается немедленно в любом случае. Но вы можете увидеть разницу между вводом 0 и 1, добавив еще одну команду:

find /
echo "find returned $?"

Способ сделать то, что вы хотите (но, вероятно, не должны делать)

Ты можешь делать, что хочешь; но эта часть моего ответа больше связана с открытием программирования оболочки, чем с решением реальной проблемы.

  • С точки зрения дизайна, перезапускаемый сигнал не тот, который вы обычно ожидаете в виде относительно простых программ, которыми обычно являются сценарии оболочки. Ожидается, что Ctrl+ Cубьет скрипт.
  • Как вы увидите ниже, в любом случае это немного расширяет возможности оболочки.

Если вы хотите , чтобы избежать убийства find, вам нужно запустить его в фоновом режиме : find / &. Затем используйте waitвстроенную функцию, чтобы дождаться нормального выхода. Сигнал будет прерывать waitвстроенную функцию, которую вы можете запустить в цикле, пока не получите сигнал, который хотите распространить. Затем используйте, killчтобы убить работу.

hell () {
  echo "Do you want to quit? Press 1 for yes and 0 for no"
  read n
  if [ "$n" = 1 ]; then
    # Kill the job if it's running, then exit
    if [ -n "$job_pid" ]; then kill $job_pid; fi
    exit 1
  fi
}

job_pid=
trap "hell" SIGINT

# Start a demo job in the background
for i in 1 2 3 4 5; do date; sleep 1; done &
job_pid=$!
# Call wait in a loop; wait will return 0 if the job exits, and 128+$signum if interrupted by a signal.
while ! wait; do
  echo "resuming wait"
done
job_pid=
echo last exit code: $?

Есть ограничения для этого подхода в оболочке:

  • Есть условие гонки: если вы нажмете Ctrl+ Cсразу после завершения задания, но перед job_pid=строкой, обработчик сигнала попытается уничтожить $jobpid, но процесс больше не существует (даже в виде зомби, потому что waitон уже был собран), и процесс Идентификатор может быть использован другим процессом. Это не легко исправить в оболочке (возможно, установив обработчик для SIGCHLD?).
  • Если вам нужен статус возврата с работы, вам нужно использовать wait $job_pidформу. Но тогда вы не сможете отличить « waitбыл прерван по сигналу» от «задания было убито по сигналу» (или от «задания прервано само по себе с возвращаемым статусом ≥128», но это общий факт в оболочке программирование).
  • Это не будет легко распространяться, если вообще на несколько подзадач. Обратите внимание, что поведение ловушек и сигналов часто удивляет, когда вы выходите за рамки основ в большинстве реализаций оболочки (только ksh делает это хорошо).

Чтобы преодолеть эти ограничения, используйте более изящный язык, такой как Perl или Python.

Жиль "ТАК - перестань быть злым"
источник