Bash
如何導致退出,從通過管道接收數據的腳本,從另一個程序?
我有用於處理數據的 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
等等。