Bash 命名管道、并行命令和退出状态

A. *_*Que 2 bash fifo exit-status

我正在尝试使用命名管道,以便可以保留stdout长进程的状态输出。我可以用于stderr状态输出,但我想保留它以防止错误。这是一个例子:

\n
#!/bin/bash\npipe=$(mktemp -u)\nmkfifo $pipe\n\ndd if=/dev/zero of=$pipe bs=1M count=1024 status=progress & cat $pipe > test.bin\n\n# \xc2\xbf\xc2\xbf\xc2\xbf Status of dd command ???\n\nrm $pipe\n
Run Code Online (Sandbox Code Playgroud)\n

该命令并不完全是我想要做的,但说明了使用命名管道将一个进程的输出馈入另一个进程的输入的组合。在我的应用程序中,dd被替换为一些长时间运行的命令,并被cat替换为ssh. 这将执行我想要的操作,但我不知道如何获取命令的状态dd$?返回命令的状态cat。如果这是一个管道,我可以使用PIPESTATUS,但这似乎不适用于并行进程。在实际应用中,其中一个(或两个)命令可能会失败。

\n

有没有办法获取并行运行的进程的状态?除了命名管道之外,是否有更好的方法来完成此任务?

\n

Sté*_*las 6

要获取异步命令的退出状态,可以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"
Run Code Online (Sandbox Code Playgroud)

(还修复了一些明显的错误,例如不检查这些mktemp/是否成功mkfifo、未加引号的扩展、缺少--s)。

请注意,如果(或在您的情况下代表的dd任何实际命令)在打开写入之前退出,则将无限期地挂起其自己的fifo 只读状态。dd$pipecatopen()

要解决这个问题,您可以在另一个 fd 上打开 shell $pipe,该进程最终将运行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=$?
Run Code Online (Sandbox Code Playgroud)

同样,如果cat在打开管道之前死亡,您将遇到相同的对称问题,可以用相同的方式解决。

如果您的系统有文件,您也可以结合使用普通管道和来检索退出状态,而不是使用命名管道:/dev/fd/n/dev/fd/nbash$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]}
Run Code Online (Sandbox Code Playgroud)

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

上面, 和ddcat将在其 fd 3( 的写入端dd和读取端cat)上打开管道,并且它们将打开并/dev/fd/3在管道上提供另一个 fd,就像上面的解决方法一样。我们使用 fds 4 和 5 来恢复两者的原始标准输入和标准输出(尽管这里cat的标准输出否则会恢复test.bin)。