Bash进程替换和同步

21

在一些Bash单元测试脚本中,我使用以下技巧来记录并显示命令的标准输出和标准错误输出:(可能与某些程序不接受输入文件的进程替换有关)

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以直接将其断言为空而不是写入文件?

更新:使用sync并不是答案 - 请参见Gilles的回答。

更新2:讨论进一步发展到Save stdout, stderr and stdout+stderr synchronously。感谢各位回答!

3个回答

31
在bash中,进程替换命令foo > >(bar)会在foo完成后立即结束,但这一点没有在文档中讨论。你可以使用以下命令进行验证。
: > >(sleep 1; echo a)

这个命令会立即返回,然后在一秒钟后异步打印a

在你的情况下,tee命令在command完成后只需要很短的时间就能完成。添加sync命令足够让tee命令完成,但这并没有消除竞争条件,就像添加sleep一样,并没有完全消除竞争条件,它只是使竞争更不可能出现。

更普遍地说,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
    

    感谢您提供这个精彩的答案,这让我成为了一个更好的管理员! - Jo Liss

    2

    有时我会使用守卫:

    : > >(sleep 1; echo a; touch guard) \
      && while true; do
        [ -f "guard" ] && { rm guard; break; }
         sleep 0.2
      done    
    

    -1
    将最后一个问题的 sync 替换为 sleep 5 或其他内容。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接