Chuyện gì đang xảy ra
Khi bạn nhấn Ctrl+ C, SIGINT
tín hiệu được gửi đến toàn bộ nhóm quy trình nền trước . Ở đây, nó được gửi đến cả find
quy trình và quy trình gọi vỏ. find
phản ứng bằng cách thoát ra ngay lập tức và vỏ phản ứng bằng cách gọi bẫy.
Nếu mã trong bẫy trả về (tức là không gọi exit
), thực thi sẽ tiến hành lệnh sau khi mã bị ngắt bởi tín hiệu. Ở đây, sau khi find
lệnh kết thúc tập lệnh, vì vậy tập lệnh sẽ thoát ngay lập tức. Nhưng bạn có thể thấy sự khác biệt giữa nhập 0 và 1 bằng cách thêm một lệnh khác:
find /
echo "find returned $?"
Một cách để làm những gì bạn muốn (nhưng có lẽ không nên làm)
Bạn có thể làm những gì bạn muốn; nhưng phần này trong câu trả lời của tôi là về khám phá lập trình shell hơn là giải quyết một vấn đề thực tế.
- Là một vấn đề thiết kế, tín hiệu có thể khởi động lại không phải là những gì bạn thường mong đợi trong các loại chương trình tương đối đơn giản mà shell script thường có. Kỳ vọng là Ctrl+ Csẽ giết tập lệnh.
- Như bạn sẽ thấy bên dưới, dù sao nó cũng mở rộng khả năng của vỏ một chút.
Nếu bạn muốn tránh bị giết find
, bạn cần khởi động nó trong nền : find / &
. Sau đó sử dụng wait
nội dung để chờ cho nó thoát ra bình thường. Một tín hiệu sẽ làm gián đoạn tích wait
hợp, mà bạn có thể chạy trong một vòng lặp cho đến khi bạn nhận được tín hiệu bạn muốn truyền đi. Sau đó sử dụng kill
để giết công việc.
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: $?
Có những hạn chế đối với phương pháp này trong trình bao:
- Có một điều kiện cuộc đua: nếu bạn nhấn Ctrl+ Cngay sau khi công việc kết thúc nhưng trước khi kết thúc
job_pid=
, trình xử lý tín hiệu sẽ cố gắng giết $jobpid
, nhưng quá trình không còn tồn tại (ngay cả khi là zombie, vì wait
đã gặt hái được nó) và quá trình ID có thể đã được sử dụng lại bởi một quy trình khác. Điều này không dễ dàng sửa chữa trong shell (có thể bằng cách đặt một trình xử lý cho SIGCHLD
?).
- Nếu bạn cần trạng thái trở lại từ công việc, bạn cần sử dụng
wait $job_pid
biểu mẫu. Nhưng sau đó, bạn không thể phân biệt wait
được bị gián đoạn bởi một tín hiệu từ người Hồi giáo, công việc đã bị giết bởi một tín hiệu khác (cũng không phải là từ công việc bị chấm dứt theo thỏa thuận của chính nó với trạng thái hoàn trả ≥128, nhưng đó là một thực tế chung lập trình).
- Điều này sẽ không dễ dàng mở rộng nếu có nhiều subjobs. Lưu ý rằng hành vi của bẫy và tín hiệu thường gây ngạc nhiên khi bạn vượt ra khỏi những điều cơ bản trong hầu hết các triển khai hệ vỏ (chỉ ksh thực hiện tốt điều này).
Để khắc phục những hạn chế này, hãy sử dụng ngôn ngữ fancier như Perl hoặc Python.