我有一个脚本,其中包含一个相当复杂的命令,其中包含多个进程替换,并且我需要从进程替换中的命令获取退出代码。我试图通过命名管道来做到这一点,但我遇到了问题,因为我以前从未使用过命名管道。
例如,给出以下命令:
somecommand <(someothercommand; echo $? > named_pipe) <(someothercommand; echo $? > named_pipe) &
somecommand
等待完成并读取所有退出代码的正确方法是什么named_pipe
(假设我不知道实际会写入多少退出代码named_pipe
)?到目前为止,我的尝试导致了阻塞somecommand
(因为我假设它正在等待从 读取某些内容named_pipe
)或仅读取一个退出代码。
答案1
实际上,这看起来不太简单。如果 shell 提供等待进程替换的支持可能是最好的,但我不认为 Bash 会这样做。
另一个问题是命名管道无法知道您要在那里写入多少行。当所有写入器都关闭时,管道将读取 EOF,但每次写入都可能会得到一个 EOF echo
。除非它们在同一时间击中,在这种情况下你就不会。
但似乎可以安排进程替换从一开始就打开一个写入 fd,以便 EOF 在它们全部完成后只出现一次。
像这样,用echo somecommand
和true
代表false
实际命令:
#!/bin/bash
dir=$(mktemp -d)
p="$dir/p"
mkfifo "$p"
# whole subshell sent to the background
(exec 3> "$p";
# both process substitutions get a copy of fd 3
echo somecommand \
<(false; echo "cmd1: $?" >&3) \
<(true; echo "cmd2: $?" >&3) \
) &
# read the exit statuses, this will see EOF once all the three
# background processes above finish
cat "$p"
rm -rf "$dir" 2>/dev/null
请注意,打印到管道的行的顺序取决于时间并且本质上是随机的。
另外,如果运行缓慢,则可以首先出现echo somecommand
的输出。cat "$p"
您需要将数据从管道读取到变量,然后wait
用于后台进程。
也有一种没有背景的可能性somecommand
,但它需要对文件句柄进行更多的体操:
#!/bin/bash
dir=$(mktemp -d)
p="$dir/p"
mkfifo "$p"
# open an fd for read+write (doesn't block because both open)
exec 3<>"$p"
# process substs inherit the fd, closing it when they exit
echo somecommand \
<(false; echo "cmd1: $?" >&3) \
<(true; echo "cmd2: $?" >&3) \
# open another reader to keep the pipe live
exec 4<"$p"
# now we can close the writing handle
exec 3>&-
# read the data off
cat <&4
exec 4<&-
rm -rf "$dir" 2>/dev/null
将退出状态收集到常规文件并读取它直到出现已知数量的行可能更直接。
#!/bin/bash
f=$(mktemp)
# number of process substitutions
n=2
echo somecommand \
<(false; echo "cmd1: $?" >>"$f") \
<(true; echo "cmd2: $?" >>"$f") \
exec 3< "$f"
# read that many lines
for ((i = 0; i < n; i++)) do
# if the data isn't there yet, retry reading until a new line appears
until read line <&3; do sleep 1; done
echo "$line";
done
exec 3<&-
rm -f "$f"
据我测试,这三个似乎都有效,但是使用进程替换和管道可能很麻烦,所以我可能错过了一些故障模式。