Що відбувається
Коли ви натискаєте 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.