如何透過管道傳輸 bash 命令並保持 Ctrl+C 工作?

如何透過管道傳輸 bash 命令並保持 Ctrl+C 工作?

考慮使用自訂命令列軟體(例如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

該特定範例的另一個選項是使用zshorksh93代替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

相關內容