Bash

如何導致退出,從通過管道接收數據的腳本,從另一個程序?

  • December 16, 2019

我有用於處理數據的 Bash 腳本,這些數據是通過管道從另一個程序發送的。

我正在嘗試添加一個在滿足特定條件後導致和退出的功能,其簡化表示為:

( while true; do date; sleep 1; done ) | ( for ((i = 0; i < 3; i++)); do read -r line; echo LINE: $line; sleep 1; done; exit 1 )

在實際情況下:

  • 管道之後的命令包含在腳本中(並被腳本替換);
  • 管道之前的命令是二進制執行檔,我無法控制它的內部。

我也嘗試關閉標準輸入:

( while true; do date; sleep 1; done ) | ( for ((i = 0; i < 3; i++)); do read -r line; echo LINE: $line; sleep 1; done; exec 0<&- )

但是,當條件滿足時,什麼都不會發送到 stdout,但左邊的程序仍然執行。

我怎樣才能完成任務?

您的管道不退出的原因是該exit 1語句僅終止右側子外殼。

在右側退出後,date您用於生成數據的命令將開始失敗,因為它無法將其輸出寫入任何地方(它會被PIPE信號殺死)。您沒有測試 的退出狀態date,因此您看不到這一點。

將左側重寫為

(
   while true; do
       if ! date; then
           exit    # or: break
       fi
       sleep 1
   done
)

或作為

(
   while date; do
       sleep 1
   done
)

將使整個管道按預期執行。

這表明您在管道左側的數據生成二進製文件沒有檢查寫入是否成功(並且它也可能忽略PIPE信號)。在右側子shell 中關閉標準輸入將無濟於事(無論如何,當您退出該子shell 時,這已經完成)。

這是生成數據的程序中的一個錯誤。

你怎麼能解決這個問題?

一種方法是使用命名管道。這將允許您獲取數據生成程序的 PID,稍後您可以使用它來殺死它:

#!/bin/bash

rm -f data.pipe
mkfifo data.pipe

( while true; do date; sleep 1; done ) >data.pipe & pid=$!

(
   for ((i = 0; i < 3; i++)); do
       read -r line
       printf 'LINE: %s\n' "$line"
       sleep 1
   done
   kill "$pid"
   exit 1
) <data.pipe

如果程序忽略了PIPE信號,它很可能忽略了其他信號,因此您必須先嘗試發送預設信號,TERM如果這不起作用,請嘗試INT等等。

引用自:https://unix.stackexchange.com/questions/556705