这是我的脚本:
#! /bin/bash
ret=0
file_out1=$(mktemp)
file_out2=$(mktemp)
(echo first start; sleep 2s; echo first finish;) &> $file_out1 &
ret=$?
P1=$!
(echo second start; sleep 1s; echo second finish;) &> $file_out2 &
ret=$?
P2=$!
wait $P1 $P2 || ret=$?
cat $file_out1
cat $file_out2
exit $ret
这里,输出是:
first start
first finish
second start
second finish
但输出应该是:
second start
second finish
first start
first finish
我的意思是,应该在完成cat $file_out1
后立即运行,并且应该在完成后立即运行。P1
cat $file_out2
P2
而且,我想确保P1成功结束,P2也成功结束。最后,我怎么知道其中是否有错误P1
或P2
两者都有错误。
在此脚本中,$ret
无法正常工作(始终如此exit 0
)。运行后,很高兴知道它$ret
是来自P1
还是来自P2
。比如说P1
有返回码x1,P2
有返回码x2。如果存在非零退出代码,则使用非零返回代码退出程序。
更新1:
一用 askubuntu 回答,建议我们可以使用--group
并行命令的参数来确保每个命令的输出等待命令完成(避免混合输出)。
它显示的命令是parallel --group 'echo -n {};sleep {};echo {}' ::: 1 3 2 4
.
该命令在我的机器上有效。但是,我不明白如何在我的上下文中使用此命令。
我的意思是我怎样才能得到
second start
second finish
first start
first finish
当输入命令是echo first start; sleep 2s; echo first finish;
和echo second start; sleep 1s; echo second finish;
使用gnu并行时。
更新2:
我目前所在的代码是:
#! /bin/bash
function first {
echo first start
sleep 2s
echo first finish
}
function second {
echo second start
sleep 1s
echo second finish
}
export -f first && export -f second
parallel -j2 ::: first second
这实际上解决了我的问题。
答案1
我想您希望每个任务在终止时立即显示输出,但避免折叠它们的输出,这就是为什么您将其缓冲在文件中 - 没关系,您也可以在输出前加上 sed 前缀,让它在出现时流动,然后根据需要再次排序:这是为了防止数据量不可预测和存储溢出的风险。
你的错误:
- 当您使用 fork 后台进程时
&
,您将无法访问其最终状态(您所期望的ret=$?
是其他内容) - 当您等待它们完成时,您无法预测哪一个将首先终止,以及您是否会
wait
按时捕获每个进程的最终状态:在进程终止后为时已晚,即使该进程的最终状态是,wait
也会得到。这是不可预测的。127
0
您的愿望是获得与顺序运行相同的反馈,但具有并行性的持续时间优势,并最大限度地减少酱汁。
让它们在自己的框中执行,管理输出竞争 -flock
文件描述符是干净的 - 并强制它们在某处记录其最终状态 - 再次,文件描述符是干净的。
编辑正如 @AhmadIsmail 编辑所说,GNU 并行是一体化解决方案。无论如何,就让这个吧。
#!/bin/bash
# wrap background
mybackground(){
tmp=$(mktemp) && trap "rm -f $tmp" RETURN
exec {safe1}>&1 {safe2}>&2 1>$tmp 2>$tmp # swap descriptors
(eval $@) # work
echo $? >&$fdstatus # report status
exec >&$safe1 2>&$safe2 # restore descriptors
flock 1 cat $tmp # report output
}
(
mybackground "echo first start; sleep 2s; echo first finish;exit 2" &
mybackground "echo second start; sleep 1s; echo second finish" &
) {fdstatus}> >(
while read rc;do ((sum+=rc));done;echo sum=$sum;exit $sum ## collect & sum status
) | cat # wait {fdstatus} termination