出于测试目的,我想将stdout和stderr 单独保存以供后续代码检查。例如,具有错误输入的测试运行应该导致输出到stderr,但是在stdout上没有输出,而具有正确输入的测试运行应该导致输出到stdout,但是没有输出到stderr。保存必须是同步的,以避免测试的竞争条件(所以我不能使用process substitution)。
为了能够在事后调试测试,我还需要在输出的序列中看到stdout和stderr。所以我必须将它们保存到同一个文件/变量/中,或者同时将它们发送到终端,以便单独保存它们。
要测试发生了哪个错误,我也需要该命令的退出代码。
出于效率和准确性的原因,我当然不能每次测试两次。
例如,是否可以将stdout重定向到stdout.log,stderr到stderr.log,和将它们都重定向到output.log中的同一命令?或者对stdout和stderr分别使用同步 tee
命令?或者将stdout和stderr的副本保存为单独的变量?
更新:看起来tim的解决方案几乎可以正常工作(修改为在终端上输出而不是记录到all.log):
$ set -o pipefail
$ {
{
echo foo | tee stdout.log 2>&3 3>&-
} 2>&1 >&4 4>&- | tee stderr.log 2>&3 3>&-
} 3>&2 4>&1
foo
$ cat stdout.log
foo
$ cat stderr.log
$ {
{
echo foo >&2 | tee stdout.log 2>&3 3>&-
} 2>&1 >&4 4>&- | tee stderr.log 2>&3 3>&-
} 3>&2 4>&1
foo
$ cat stdout.log
$ cat stderr.log
foo
$ bar=$({
{
echo foo | tee stdout.log 2>&3 3>&-
} 2>&1 >&4 4>&- | tee stderr.log 2>&3 3>&-
} 3>&2 4>&1)
$ echo "$bar"
foo
$ cat stdout.log
foo
$ cat stderr.log
$ bar=$({
{
echo foo >&2 | tee stdout.log 2>&3 3>&-
} 2>&1 >&4 4>&- | tee stderr.log 2>&3 3>&-
} 3>&2 4>&1)
$ cat stdout.log
$ cat stderr.log
foo
$ echo "$bar"
foo
除了最后一次迭代之外,这似乎有效,其中bar
的值设置为stderr的内容。有关所有这些工作的建议吗?
答案 0 :(得分:11)
请参阅BashFAQ/106。吃蛋糕也不容易吃。
从该页面开始:
但是有些人不会接受stdout和stderr之间的分离丢失,或者行的去同步化。他们是纯粹主义者,因此他们要求最困难的形式 - 我想将stdout和stderr一起记录到一个文件中,但我也希望他们保持原来的独立目的地。
为了做到这一点,我们首先要做一些注意事项:
- 如果要有两个单独的stdout和stderr流,那么一些进程必须编写每个流。
- 没有办法在shell脚本中编写一个进程,只要其中一个有可用的输入就从两个单独的FD中读取,因为shell没有poll(2)或select(2)接口。
- 因此,我们需要两个独立的编写程序。
- 保持两个独立编写器的输出不会相互破坏的唯一方法是确保它们都以追加模式打开输出。在附加模式下打开的FD具有保证的属性,每次向其写入数据时,它将首先跳到最后。
所以:
# Bash
> mylog
exec > >(tee -a mylog) 2> >(tee -a mylog >&2)
echo A >&2
cat file
echo B >&2
这可确保日志文件正确无误。它不能保证编写器在下一个shell提示符之前完成:
~$ ./foo
A
hi mom
B
~$ cat mylog
A
hi mom
B
~$ ./foo
A
hi mom
~$ B
另请参阅BashFAQ/002和BashFAQ/047。
答案 1 :(得分:2)
这听起来像是一个多任务的任务。
要在不使用进程替换的情况下测试同步输出或磁盘写入,您可能需要使用类似于以下内容的Bash shell重定向技巧和tee(1):
echos() { echo "hello on stdout"; echo "hello on stderr" 1>&2; return 0; }
{
{
echos 3>&- |
tee stdout.log 2>&3 3>&-
} 2>&1 >&4 4>&- |
tee stderr.log 2>&3 3>&-
} 3>&2 4>&1 2>&1 | tee /dev/stderr > all.log
open -e stdout.log stderr.log all.log # Mac OS X
有关详细信息,请参阅:http://codesnippets.joyent.com/posts/show/8769
要获取命令的退出代码,您可能需要分别检查pipefail或PIPESTATUS(除了last-exit-code变量“$?”):
help set | grep -i pipefail
man bash | less -p pipefail
man bash | less -p PIPESTATUS
答案 2 :(得分:1)
对于最后一次迭代,bar的值设置为stderr的内容,请尝试:
# restore original stdout & stderr at the end by adding: 1>&2 2>&1
bar="$(
{
{
echo foo >&2 | tee stdout.log 2>&3 3>&-
} 2>&1 >&4 4>&- | tee stderr.log 2>&3 3>&-
} 3>&2 4>&1 1>&2 2>&1
)"
echo "$bar"