(Lấy cảm hứng từ câu trả lời của Gilles)
Với ISIGcờ được đặt, cách duy nhất để Childtập lệnh nhận được SIGINTmà không có cha mẹ nhận được SIGINTlà 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 -mtùy chọn.
Nếu bạn bật -mtùy chọn trong Childtậ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 SIGINTkhi INTRký tự được đọc.
Dưới đây là mô tả POSIX của -mtùy chọn :
-mTù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 -mtù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 -ikhông.
Thí dụ:
các Parentkị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 Childkị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 Childchờ đầ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 SIGINTxử 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 Parentthay vì Child, bạn có thể làm điều này:
các Parentkị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 Childkị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
SIGINTtrong 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
Childthà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, QUITvà SUSPký 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 0khi 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 Childkị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 setpgidtiệ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ỗiSIGQUITvàSIGTERMbị bỏ qua.