(Lấy cảm hứng từ câu trả lời của Gilles)
Với ISIG
cờ được đặt, cách duy nhất để Child
tập lệnh nhận được SIGINT
mà không có cha mẹ nhận được SIGINT
là tập lệnh nằm trong nhóm quy trình riêng. Điều này có thể được thực hiện với các set -m
tùy chọn.
Nếu bạn bật -m
tùy chọn trong Child
tập lệnh shell, nó sẽ thực hiện kiểm soát công việc mà không cần tương tác. Điều này sẽ khiến nó chạy các thứ trong một nhóm quy trình riêng biệt, ngăn phụ huynh nhận được SIGINT
khi INTR
ký tự được đọc.
Dưới đây là mô tả POSIX của -m
tùy chọn :
-m
Tùy chọn này sẽ được hỗ trợ nếu việc triển khai hỗ trợ tùy chọn Tiện ích di động người dùng. Tất cả các công việc sẽ được chạy trong các nhóm quy trình riêng của họ. Ngay trước khi shell phát ra lời nhắc sau khi hoàn thành công việc nền, một thông báo báo cáo trạng thái thoát của công việc nền sẽ được ghi vào lỗi tiêu chuẩn. Nếu một công việc nền trước dừng lại, trình bao sẽ viết một thông báo lỗi tiêu chuẩn cho hiệu ứng đó, được định dạng như mô tả bởi tiện ích công việc. Ngoài ra, nếu một công việc thay đổi trạng thái khác với thoát (ví dụ: nếu nó dừng cho đầu vào hoặc đầu ra hoặc bị dừng bởi tín hiệu SIGSTOP), trình bao sẽ viết một thông báo tương tự ngay trước khi viết lời nhắc tiếp theo. Tùy chọn này được bật theo mặc định cho các vỏ tương tác.
Các -m
tùy chọn tương tự như -i
, nhưng nó không làm thay đổi hành vi của vỏ gần càng nhiều càng -i
không.
Thí dụ:
các Parent
kịch bản:
#!/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"
các Child
kịch bản:
#!/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"
Đây là những gì xảy ra khi bạn nhấn Control+ Ctrong khi Child
chờ đầu vào:
$ ./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
Lưu ý cách SIGINT
xử lý của cha mẹ không bao giờ được thực thi.
Ngoài ra, nếu bạn muốn sửa đổi Parent
thay vì Child
, bạn có thể làm điều này:
các Parent
kịch bản:
#!/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"
các Child
kịch bản (bình thường, không có nhu cầu -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"
Ý tưởng thay thế
- Sửa đổi các quy trình khác trong nhóm quy trình tiền cảnh để bỏ qua
SIGINT
trong thời gian Child
. Điều này không giải quyết câu hỏi của bạn, nhưng nó có thể giúp bạn có được những gì bạn muốn.
- Sửa đổi
Child
thành:
- Sử dụng
stty -g
để sao lưu các thiết lập thiết bị đầu cuối hiện tại.
- Chạy
stty -isig
để không tạo ra tín hiệu với INTR
, QUIT
và SUSP
ký tự.
- Trong nền, đọc đầu vào đầu cuối và tự gửi tín hiệu cho phù hợp (ví dụ: chạy
kill -QUIT 0
khi Control+ \được đọc, kill -INT $$
khi Control+ Cđược đọc). Điều này không phải là nhỏ, và có thể không làm cho nó hoạt động trơn tru nếu Child
kịch bản hoặc bất cứ thứ gì nó chạy có nghĩa là tương tác.
- Khôi phục cài đặt thiết bị đầu cuối trước khi thoát (lý tưởng nhất là từ bẫy
EXIT
).
- Tương tự như số 2 ngoại trừ thay vì chạy
stty -isig
, hãy đợi người dùng nhấn Enterhoặc một số phím không đặc biệt khác trước khi giết Child
.
Viết setpgid
tiện ích của riêng bạn bằng C, Python, Perl, v.v. mà bạn có thể sử dụng để gọi setpgid()
. Đây là một triển khai C thô:
#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;
}
Ví dụ sử dụng từ 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
). Ví dụ:${ENV}
có nguồn gốc, shell sẽ không thoát ngay lập tức khi gặp lỗiSIGQUIT
vàSIGTERM
bị bỏ qua.