(Вдохновленный ответом Жиля)
При установленном ISIGфлаге единственный способ Childполучить сценарий SIGINTбез получения родительского элемента SIGINT- это войти в свою собственную группу процессов. Это может быть достигнуто с set -mопцией.
Если вы включите -mопцию в Childсценарии оболочки, он будет выполнять управление заданиями, не будучи интерактивным. Это заставит его запускать вещи в отдельной группе процессов, предотвращая получение родительского элемента, SIGINTкогда INTRчитается символ.
Вот описание -mопции POSIX :
-mЭта опция должна поддерживаться, если реализация поддерживает опцию User Portability Utilities. Все задания должны выполняться в своих собственных группах процессов. Непосредственно перед тем, как оболочка выдает подсказку после завершения фонового задания, сообщение о состоянии завершения фонового задания должно быть записано со стандартной ошибкой. Если приоритетное задание останавливается, оболочка должна записать сообщение со стандартной ошибкой на этот счет в формате, описанном утилитой заданий. Кроме того, если задание изменяет состояние, отличное от выхода (например, если оно останавливается для ввода или вывода или останавливается сигналом SIGSTOP), оболочка должна написать аналогичное сообщение непосредственно перед написанием следующего приглашения. Эта опция включена по умолчанию для интерактивных оболочек.
-mВариант похож -i, но это не меняет поведение оболочечных почти столько же , сколько -iделает.
Пример:
Parentскрипт:
#!/bin/sh
trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
echo "PARENT: pid=$$"
echo "PARENT: Spawning child..."
./Child
echo "PARENT: child returned"
echo "PARENT: exiting normally"
Childскрипт:
#!/bin/sh -m
# ^^
# notice the -m option above!
trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
echo "CHILD: pid=$$"
echo "CHILD: hit enter to exit"
read foo
echo "CHILD: exiting normally"
Вот что происходит, когда вы нажимаете Control+, Cпока Childожидает ввода:
$ ./Parent
PARENT: pid=12233
PARENT: Spawning child...
CHILD: pid=12234
CHILD: hit enter to exit
^CCHILD: caught SIGINT; exiting
PARENT: child returned
PARENT: exiting normally
Обратите внимание, как SIGINTобработчик родителя никогда не выполняется.
Кроме того, если вы хотите изменить Parentвместо Child, вы можете сделать это:
Parentскрипт:
#!/bin/sh
trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
echo "PARENT: pid=$$"
echo "PARENT: Spawning child..."
sh -m ./Child # or 'sh -m -c ./Child' if Child isn't a shell script
echo "PARENT: child returned"
echo "PARENT: exiting normally"
Childскрипт (нормальный, нет необходимости -m):
#!/bin/sh
trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
echo "CHILD: pid=$$"
echo "CHILD: hit enter to exit"
read foo
echo "CHILD: exiting normally"
Альтернативные идеи
- Измените другие процессы в группе процессов переднего плана, чтобы игнорировать их
SIGINTв течение Child. Это не отвечает на ваш вопрос, но может дать вам то, что вы хотите.
- Изменить
Childна:
- Используйте
stty -gдля резервного копирования текущих настроек терминала.
- Бегите ,
stty -isigчтобы не генерировать сигналы с INTR, QUITи SUSPперсонажами.
- В фоновом режиме прочитайте входной сигнал терминала и отправьте сигналы самостоятельно (например, запустите,
kill -QUIT 0когда Control+ \читается, kill -INT $$когда Control+ Cчитается). Это не тривиально, и может быть невозможно заставить это работать гладко, если Childсценарий или все, что он выполняет, должно быть интерактивным.
- Восстановите настройки терминала перед выходом (в идеале из ловушки
EXIT).
- То же, что №2, но вместо того, чтобы работать
stty -isig, подождите, пока пользователь нажмет Enterили другой не специальный ключ, прежде чем убивать Child.
Напишите свою собственную setpgidутилиту на C, Python, Perl и т. Д., Которую вы можете использовать для вызова setpgid(). Вот грубая реализация C:
#define _XOPEN_SOURCE 700
#include <unistd.h>
#include <signal.h>
int
main(int argc, char *argv[])
{
// todo: add error checking
void (*backup)(int);
setpgid(0, 0);
backup = signal(SIGTTOU, SIG_IGN);
tcsetpgrp(0, getpid());
signal(SIGTTOU, backup);
execvp(argv[1], argv + 1);
return 1;
}
Пример использования от Child:
#!/bin/sh
[ "${DID_SETPGID}" = true ] || {
# restart self after calling setpgid(0, 0)
exec env DID_SETPGID=true setpgid "$0" "$@"
# exec failed if control reached this point
exit 1
}
unset DID_SETPGID
# do stuff here
ksh). Примеры:${ENV}источник получен, оболочка не выйдет сразу, когда обнаружит ошибку,SIGQUITиSIGTERMигнорируется.