2013-08-28 22 views
11

script1.sh:Bash: Tại sao tập lệnh mẹ không chấm dứt trên SIGINT khi tập lệnh con bẫy SIGINT?

#!/bin/bash  

./script2.sh 
echo after-script 

script2.sh:

#!/bin/bash 

function handler { 
    exit 130 
} 

trap handler SIGINT 

while true; do true; done 

Khi tôi bắt đầu script1.sh từ một thiết bị đầu cuối, và sau đó sử dụng tổ hợp phím Ctrl + C để gửi SIGINT vào nhóm quá trình của nó, tín hiệu bị mắc kẹt bởi script2.sh và khi script2.sh chấm dứt, script1.sh in "after-script". Tuy nhiên, tôi đã mong đợi script1.sh để ngay lập tức chấm dứt sau khi dòng gọi script2.sh. Tại sao đây không phải là trường hợp trong ví dụ này?

nhận xét bổ sung (chỉnh sửa):

  • Như script1.sh và script2.sh đều nằm trong nhóm quá trình tương tự, SIGINT được gửi đến cả kịch bản khi Ctrl + C được nhấn trên dòng lệnh . Đó là lý do tại sao tôi không mong đợi script1.sh để tiếp tục khi script2.sh thoát.

  • Khi dòng "xử lý bẫy SIGINT" trong script2.sh được nhận xét, script1.sh không thoát ngay sau khi script2.sh tồn tại. Tôi muốn biết lý do tại sao nó hoạt động khác nhau sau đó, như script2.sh sản xuất chỉ là cùng một mã thoát (130) sau đó.

+1

Có lẽ sử dụng 'set -e'? – phs

Trả lời

10

câu trả lời mới:

Câu hỏi này là rất nhiều thú vị hơn tôi ban đầu bị nghi ngờ. Câu trả lời về cơ bản được đưa ra ở đây:

What happens to a SIGINT (^C) when sent to a perl script containing children?

Đây là miếng ngon có liên quan. Tôi nhận ra rằng bạn không sử dụng Perl, nhưng tôi cho rằng Bash đang sử dụng quy ước của C.

Chức năng hệ thống dựng sẵn của Perl hoạt động giống như chức năng hệ thống C (3) từ thư viện C chuẩn khi có liên quan đến tín hiệu. Nếu bạn đang sử dụng phiên bản hệ thống() hoặc đường ống mở hoặc ngược của Perl, thì cha/mẹ - hệ thống gọi điện thoại thay vì được gọi là - sẽ loại bỏ bất kỳ SIGINT và SIGQUIT nào khi trẻ đang hoạt động .

This explanation là tốt nhất tôi đã thấy về các lựa chọn khác nhau có thể được thực hiện. Nó cũng nói rằng Bash thực hiện phương pháp WCE. Đó là, khi một tiến trình cha nhận được SIGINT, nó đợi cho đến khi tiến trình con của nó trả về. Nếu quá trình đó được xử lý thoát khỏi SIGINT, nó cũng thoát với SIGINT. Nếu đứa trẻ thoát khỏi bất kỳ cách nào khác, nó bỏ qua SIGINT.

Ngoài ra còn có một cách mà vỏ gọi có thể nói cho dù được gọi là chương trình thoát trên SIGINT và nếu nó bỏ qua SIGINT (hoặc sử dụng nó cho mục đích khác). Như trong cách WUE, vỏ chờ đứa trẻ hoàn thành . Nó cho biết liệu chương trình đã kết thúc vào SIGINT và nếu như vậy, nó sẽ ngừng tập lệnh. Nếu chương trình đã thực hiện bất kỳ lối ra nào khác, tập lệnh sẽ tiếp tục. Tôi sẽ gọi cách làm những việc "WCE" (cho "chờ đợi và hợp tác thoát") cho phần còn lại của tài liệu này.

Tôi không thể tìm thấy tham chiếu đến điều này trong trang người dùng Bash, nhưng tôi sẽ tiếp tục tìm trong tài liệu thông tin. Nhưng tôi tin chắc 99% đây là câu trả lời đúng.

Cũ câu trả lời:

Một trạng thái thoát khác không từ một lệnh trong một kịch bản Bash không chấm dứt chương trình. Nếu bạn thực hiện một số echo $? sau ./script2.sh, nó sẽ hiển thị 130. Bạn có thể chấm dứt tập lệnh bằng cách sử dụng set -e làm các gợi ý.

$ help set 
... 
-e Exit immediately if a command exits with a non-zero status. 
+0

Điều đó không hoàn toàn trả lời câu hỏi. Lưu ý rằng SIGINT được gửi đến cả script1.sh và script2.sh khi nhấn Ctrl + C trên thiết bị đầu cuối. Tôi đã thêm một số nhận xét bổ sung cho câu hỏi. –

+0

Điều đó rất thú vị. Tôi vẫn tự hỏi về sự khác biệt giữa các phiên bản có và không có "bộ xử lý bẫy SIGINT" mặc dù. Tôi nghi ngờ rằng tín hiệu đầu tiên được gửi đến đứa trẻ và sau đó là cho phụ huynh. Khi một trình xử lý bẫy được cài đặt trong đứa trẻ, có thể mất nhiều thời gian hơn để chấm dứt, và do đó nó vẫn còn sống khi cha mẹ nhận được tín hiệu (mà nó bỏ qua). Nếu không có một handler, đứa trẻ có lẽ đã chấm dứt khi cha mẹ nhận được tín hiệu, do đó _not_ bỏ qua nó và chấm dứt. Có ai có thể xác nhận dự đoán của tôi không? –

+0

Đoán của bạn không chính xác. Tôi đã cập nhật câu trả lời của mình. – seanmcl

0

Lý do là script1.sh của bạn không kết thúc là script2.sh đang chạy trong một vỏ. Để làm cho lối ra kịch bản cũ, bạn có thể thiết -e theo đề nghị của PHS và seanmcl gây ra hoặc buộc script2.sh để chạy trong cùng một vỏ bằng cách nói:

. ./script2.sh 

trong kịch bản đầu tiên của bạn. Những gì bạn đang quan sát sẽ rõ ràng nếu bạn đã làm set -x trước khi thực thi tập lệnh của mình. help set nói:

-x Print commands and their arguments as they are executed. 
0

Bạn cũng có thể cho kịch bản thứ hai của bạn gửi một tín hiệu chấm dứt trên kịch bản mẹ của nó bởi SIGHUP, hoặc tín hiệu an toàn và có thể sử dụng khác như SIGQUIT trong đó kịch bản phụ huynh có thể xem xét hoặc bẫy cũng (gửi SIGINT không hoạt động).

script1.sh:

#!/bin/bash 

trap 'exit 0' SIQUIT ## We could also just accept SIGHUP if we like without traps but that sends a message to the screen. 

./script2.sh ## or "bash script.sh" or "(. ./script.sh;) which would run it on another process 
echo after-script 

script2.sh:

#!/bin/bash 

SLEEPPID='' 

PID=$BASHPID 
read PPID_ < <(exec ps -p "$PID" -o "$ppid=") 

function handler { 
    [[ -n $SLEEPPID ]] && kill -s SIGTERM "$SLEEPPID" &>/dev/null 
    kill -s SIGQUIT "$PPID_" 
    exit 130 
} 

trap handler SIGINT 

# better do some sleeping: 

for ((;;)); do 
    [[ -n $SLEEPPID ]] && kill -s 0 "$SLEEPPID" &>/dev/null || { 
    sleep 20 & 
    SLEEPPID=$! 
    } 
    wait 
done 

gốc dòng cuối cùng của bạn trong script1.sh của bạn có thể chỉ có như thế này cũng tùy thuộc vào kịch bản của bạn có ý định thực hiện.

./script2.sh || exit 
... 

Hoặc

./script2.sh 
[[ $? -eq 130 ]] && exit 
... 
1

Phần thứ hai của @ seanmcl được cập nhật câu trả lời là chính xác và liên kết để http://www.cons.org/cracauer/sigint.html là một thực sự tốt để đọc qua một cách cẩn thận.

Từ liên kết đó, "Bạn không thể 'giả' trạng thái thoát thích hợp bằng lối ra (3) với giá trị số đặc biệt, ngay cả khi bạn tra cứu giá trị số cho hệ thống". Trên thực tế, đó là những gì đang được thử trong script2.sh của @Hermann Speiche.

Một câu trả lời là để sửa đổi điều khiển chức năng trong script2.sh như sau:

function handler { 
    # ... do stuff ... 
    trap INT 
    kill -2 $$ 
} 

này có hiệu quả loại bỏ các xử lý tín hiệu và "rethrows" các SIGINT, làm cho quá trình bash để thoát với những lá cờ thích hợp như vậy quá trình bash gốc của nó sau đó xử lý chính xác SIGINT mà ban đầu được gửi đến nó. Bằng cách này, sử dụng set -e hoặc bất kỳ hack nào khác không thực sự được yêu cầu. Cũng cần lưu ý rằng nếu bạn có một tệp thực thi hoạt động không đúng khi gửi SIGINT (nó không tuân theo "Làm thế nào để trở thành một chương trình thích hợp" trong liên kết ở trên, ví dụ:nó thoát với một mã trả về bình thường), một cách để làm việc xung quanh việc này là kết thúc cuộc gọi đến quá trình đó bằng một kịch bản như sau:

#!/bin/bash 

function handler { 
    trap INT 
    kill -2 $$ 
} 

trap handler INT 
badprocess "[email protected]" 
Các vấn đề liên quan