考慮使用自訂命令列軟體(例如loopHelloWorld)來檢測ctrl+C 以實現良好的關閉。如何在不丟失 的情況下透過管道傳遞答案Ctrl+C
?
$ loopHelloWorld
- Ctrl+C to nicely shutdown
但是使用管道時,管道會殺死軟體而不會很好地關閉
$ loopHelloWorld |
while IFS= read -r line; do
echo "$line"
done
例子
ping example.com |
while IFS= read -r line; do
echo "$line"
done
答案1
Ctrl+C導致 SIGINT 被傳送到管道中的所有進程(因為它們都在與互動式 shell 的前台作業相對應的相同進程組中運行)。
所以在:
loopHelloWorld |
while IFS= read -r line; do
echo "$line"
done
正在運行的進程loopHelloWorld
和運行循環的子 shell 的進程都while
將獲得SIGINT
.
如果將訊息loopHelloWorld
寫入Ctrl+C to nicely shutdown
其標準輸出,它也會被寫入管道。如果那是後另一端的子 shell 已經死亡,那麼loopHelloWorld
將會也收到一個 SIGPIPE,您需要處理它。
在這裡,您應該將該訊息寫入 stderr,因為它不是命令的正常輸出(但不適用於該ping
範例)。那麼它就不會通過管道。
或者您可以讓執行 while 迴圈的子 shell 忽略 SIGINT,以便它loopHelloWorld
在 SIGINT 之後繼續讀取輸出:
loopHelloWorld | (
trap '' INT
while IFS= read -r line; do
printf '%s\n' "$line"
done
)
然而,當您按 時,這會導致管道的退出狀態為 0 Ctrl+C。
該特定範例的另一個選項是使用zsh
orksh93
代替bash
。在這些 shell 中,while
循環將在主 shell 進程中運行,因此不會受到 SIGINT 的影響。
loopHelloWorld | cat
這對於在前台進程組中運行cat
並沒有幫助。loopHelloWorld
答案2
使用陷阱:
#! /bin/bash
trap handleInt SIGINT
interrupted=0
function handleInt {
echo "Interrupted..."
interrupted=1
}
while true
do
[[ interrupted -ne 0 ]] && { echo "Ctrl-C caught, exiting..." ; exit ; }
echo "Sleeping..."
sleep 1
read -r line
echo "$line"
done