Bash

Bash 命名管道、並行命令和退出狀態

  • February 8, 2022

我正在嘗試使用命名管道,以便我可以保留stdout長時間過程的狀態輸出。我可以stderr用於狀態輸出,但我想保留它以防出錯。這是一個例子:

#!/bin/bash
pipe=$(mktemp -u)
mkfifo $pipe

dd if=/dev/zero of=$pipe bs=1M count=1024 status=progress & cat $pipe > test.bin

# ¿¿¿ Status of dd command ???

rm $pipe

該命令並不完全是我想要做的,但說明了使用命名管道將一個程序的輸出饋送到另一個程序的輸入的組合。在我的應用程序中,dd替換為一些長時間執行的命令,並cat替換為ssh. 這會做我想要的,但我不知道如何獲取dd命令的狀態,因為它$?會返回命令的狀態cat。如果這是一個管道,我可以使用PIPESTATUS,但這似乎不適用於並行程序。在實際應用中,任何一個(或兩個)命令都可能失敗。

有沒有辦法讓程序的狀態並行執行?有沒有比命名管道更好的方法來完成這個任務?

要獲取非同步命令的退出狀態,請wait在其 pid 上使用:

#!/bin/bash -
pipe=$(mktemp -u) || exit
mkfifo -m 600 -- "$pipe" || exit

dd if=/dev/zero of="$pipe" bs=1M count=1024 status=progress &
dd_pid=$!

cat -- "$pipe" > test.bin
cat_status=$?
wait "$dd_pid"
dd_status=$?

rm -f -- "$pipe"

(還修復了一些明顯的錯誤,例如不檢查那些mktemp/的成功mkfifo,未引用的擴展,缺少--s)。

請注意,如果(或在您的情況下替代的dd任何實際命令)在打開寫入之前退出,則將無限期地掛在它自己的只讀FIFO上。dd``$pipe``cat``open()

要解決這個問題,您可以$pipe在另一個 fd 上為最終執行的程序打開 shell,dd確保管道在cat打開時立即實例化(並假設dd在打開之前不任意關閉該 fd $pipe):

dd if=/dev/zero 3> "$pipe" of="$pipe" bs=1M count=1024 status=progress &
dd_pid=$!
cat -- "$pipe" > test.bin
cat_status=$?
wait "$dd_pid"
dd_status=$?

同樣,如果cat在打開管道之前死亡,您將遇到相同的對稱問題,您可以以相同的方式解決。

如果您的系統有文件,而不是命名管道,您可能還可以使用普通管道並使用’s來檢索退出狀態:/dev/fd/*n*``/dev/fd/*n*``bash``$PIPESTATUS

{
 dd if=/dev/zero of=/dev/fd/3 bs=1M count=1024 3>&1 >&5 4<&- 5>&- |
   cat /dev/fd/3 3<&0 <&4 4<&- 5>&- > test.bin
} 4<&0 5>&1

dd_status=${PIPESTATUS[0]} cat_status=${PIPESTATUS[1]}

$PIPESTATUS是特定於 bash 的,但有其他 shell 的替代方案)。

上面,ddcat都將在他們的 fd 3 上打開管道(對於 的寫入端dd和對於 的讀取端cat),並且他們將/dev/fd/3在管道上打開另一個 fd,就像上面的解決方法一樣。我們使用 fds 4 和 5 來恢復兩者的原始標準輸入和標準輸出(儘管這裡cat的標準輸出是另外的test.bin)。

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