Shell-Script
為什麼 SIGINT 不能在腳本中的後台程序上工作?
我在腳本中有以下內容:
yes >/dev/null & pid=$! echo $pid sleep 2 kill -INT $pid sleep 2 ps aux | grep yes
當我執行它時,輸出顯示它
yes
在腳本結束時仍在執行。但是,如果我以互動方式執行命令,則程序成功終止,如下所示:> yes >/dev/null & [1] 9967 > kill -INT 9967 > ps aux | grep yes sean ... 0:00 grep yes
為什麼 SIGINT 會終止互動式實例中的程序,而不是腳本實例中的程序?
編輯
以下是一些可能有助於診斷問題的補充資訊。我編寫了下面的 Go 程序來模擬上面的腳本。
package main import ( "fmt" "os" "os/exec" "time" ) func main() { yes := exec.Command("yes") if err := yes.Start(); err != nil { die("%v", err) } time.Sleep(time.Second*2) kill := exec.Command("kill", "-INT", fmt.Sprintf("%d", yes.Process.Pid)) if err := kill.Run(); err != nil { die("%v", err) } time.Sleep(time.Second*2) out, err := exec.Command("bash", "-c", "ps aux | grep yes").CombinedOutput() if err != nil { die("%v", err) } fmt.Println(string(out)) } func die(msg string, args ...interface{}) { fmt.Fprintf(os.Stderr, msg+"\n", args...) os.Exit(1) }
我將它建構為腳本
main
並./main
在腳本中執行,並以互動方式執行並提供相同的以下輸出./main
:./main &
sean ... 0:01 [yes] <defunct> sean ... 0:00 bash -c ps aux | grep yes sean ... 0:00 grep yes
但是,
./main &
在腳本中執行會給出以下結果:sean ... 0:03 yes sean ... 0:00 bash -c ps aux | grep yes sean ... 0:00 grep yes
這讓我相信差異與 Bash 自己的作業控制無關,儘管我在 Bash shell 中執行所有這些。
使用什麼 shell 是一個問題,因為不同的 shell 處理作業控制的方式不同(並且作業控制很複雜;
job.c
目前bash
根據 3,300 行 C 語言cloc
)。pdksh
例如,Mac OS X 10.11 上的5.2.14 與bash
3.2 顯示:$ cat code pkill yes yes >/dev/null & pid=$! echo $pid sleep 2 kill -INT $pid sleep 2 pgrep yes $ bash code 38643 38643 $ ksh code 38650 $
這裡也相關的是不
yes
執行信號處理,因此繼承了從父 shell 程序繼承的任何內容;相比之下,如果我們確實執行信號處理——$ cat sighandlingcode perl -e '$SIG{INT} = sub { die "ouch\n" }; sleep 5' & pid=$! sleep 2 kill -INT $pid $ bash sighandlingcode ouch $ ksh sighandlingcode ouch $
— SIGINT 被觸發,無論父 shell,因為
perl
這裡不像yes
改變了信號處理。有一些與信號處理相關的系統呼叫,可以通過 DTrace 或strace
在 Linux 上觀察到:-bash-4.2$ cat code pkill yes yes >/dev/null & pid=$! echo $pid sleep 2 kill -INT $pid sleep 2 pgrep yes pkill yes -bash-4.2$ rm foo*; strace -o foo -ff bash code 21899 21899 code: line 9: 21899 Terminated yes > /dev/null -bash-4.2$
我們發現這個
yes
過程最終SIGINT
被忽略:-bash-4.2$ egrep 'exec.*yes' foo.21* foo.21898:execve("/usr/bin/pkill", ["pkill", "yes"], [/* 24 vars */]) = 0 foo.21899:execve("/usr/bin/yes", ["yes"], [/* 24 vars */]) = 0 foo.21903:execve("/usr/bin/pgrep", ["pgrep", "yes"], [/* 24 vars */]) = 0 foo.21904:execve("/usr/bin/pkill", ["pkill", "yes"], [/* 24 vars */]) = 0 -bash-4.2$ grep INT foo.21899 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, 8) = 0 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, 8) = 0 rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7f18ebee0250}, {SIG_DFL, [], SA_RESTORER, 0x7f18ebee0250}, 8) = 0 --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=21897, si_uid=1000} --- -bash-4.2$
用程式碼重複這個測試,
perl
你應該看到SIGINT
沒有被忽略,或者也pdksh
沒有像bash
. 像在互動模式中一樣打開“監視模式”時bash
,yes
被殺死。-bash-4.2$ cat monitorcode #!/bin/bash set -m pkill yes yes >/dev/null & pid=$! echo $pid sleep 2 kill -INT $pid sleep 2 pgrep yes pkill yes -bash-4.2$ ./monitorcode 22117 [1]+ Interrupt yes > /dev/null -bash-4.2$