підтверджений вихід за допомогою пастки


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 /

Відповіді:


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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.