(可能与Do some programs not accept process substitution for input files?相关)
在一些Bash单元测试脚本中,我使用以下技巧来记录和显示命令的stdout和stderr:
command > >(tee "${stdoutF}") 2> >(tee "${stderrF}" >&2)
此过程会向stdout生成一些输出,因此$stdoutF
文件会获取一些数据。然后我运行另一个不输出任何数据的命令:
diff -r "$source" "$target" > >(tee "${stdoutF}") 2> >(tee "${stderrF}" >&2)
但是,在运行空白测试(使用shunit-ng)之前,此过程看起来并不总是成功完成:
assertNull 'Unexpected output to stdout' "$(<"$stdoutF")"
在100次运行测试中,这次失败了25次。
在测试文件空虚之前调用sync
是否足够:
sync
assertNull 'Unexpected output to stdout' "$(<"$stdoutF")"
...和/或它应该通过强制执行命令序列来工作:
diff -r "$source" "$target" \
> >(tee "${stdoutF}"; assertNull 'Unexpected output to stdout' "$(<"$stdoutF")")
2> >(tee "${stderrF}" >&2)
...和/或是否可以tee
以某种方式直接assertNull
而不是文件?
更新:sync
不是答案 - 请参阅下面的Gilles回复。
更新2 :进一步讨论Save stdout, stderr and stdout+stderr synchronously。谢谢你的答案!
答案 0 :(得分:27)
在bash中,只要foo > >(bar)
完成,进程替换替换命令foo
就会完成。 (文档中未对此进行讨论。)您可以使用
: > >(sleep 1; echo a)
此命令立即返回,然后在一秒后异步打印a
。
在您的情况下,tee
命令只需要一点点时间就可以完成command
。添加sync
会让tee
有足够的时间来完成,但这不会消除竞争条件,只不过添加sleep
就可以了,它只会使竞赛更不可能显现。< / p>
更一般地说,sync
没有任何内部可观察的影响:如果您想访问文件系统存储在不同操作系统实例下的设备,它只会产生影响。更清楚地说,如果您的系统断电,只有在重新启动后才能保证在最后sync
之前写入的数据可用。
至于消除竞争条件,以下是一些可能的方法:
明确同步所有替换过程。
mkfifo sync.pipe
command > >(tee -- "$stdoutF"; echo >sync.pipe)
2> >(tee -- "$stderrF"; echo >sync.pipe)
read line < sync.pipe; read line < sync.pipe
为每个命令使用不同的临时文件名,而不是重用$stdoutF
和$stderrF
,并强制始终新创建临时文件。
放弃进程替换并改用管道。
{ { command | tee -- "$stdoutF" 1>&3; } 2>&1 \
| tee -- "$stderrF" 1>&2; } 3>&1
如果您需要命令的返回状态,bash会将其放入${PIPESTATUS[0]}
。
{ { command | tee -- "$stdoutF" 1>&3; exit ${PIPESTATUS[0]}; } 2>&1 \
| tee -- "$stderrF" 1>&2; } 3>&1
if [ ${PIPESTATUS[0]} -ne 0 ]; then echo command failed; fi
答案 1 :(得分:1)
我有时会放一个警卫:
: > >(sleep 1; echo a; touch guard) \
&& while true; do
[ -f "guard" ] && { rm guard; break; }
sleep 0.2
done
答案 2 :(得分:-1)
插入sleep 5
或其他代替sync
来回答您的上一个问题