Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/bash/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Bash 同步保存stdout、stderr和stdout+stderr_Bash_Redirect - Fatal编程技术网

Bash 同步保存stdout、stderr和stdout+stderr

Bash 同步保存stdout、stderr和stdout+stderr,bash,redirect,Bash,Redirect,出于测试目的,我想分别保存stdout和stderr,以供后续代码检查。例如,输入错误的测试运行应该输出到stderr,但在stdout上没有结果,而输入正确的测试运行应该输出到stdout,但在stderr上没有结果。保存必须是同步的,以避免测试的竞争条件,所以我不能使用 为了能够在事后调试测试,我还需要按照输出的顺序查看stdout和stderr。因此,我要么将它们都保存到同一个文件/变量/任何内容中,要么在分别保存它们的同时将它们发送到终端 为了测试发生了哪个错误,我还需要命令的退出代码

出于测试目的,我想分别保存stdout和stderr,以供后续代码检查。例如,输入错误的测试运行应该输出到stderr,但在stdout上没有结果,而输入正确的测试运行应该输出到stdout,但在stderr上没有结果。保存必须是同步的,以避免测试的竞争条件,所以我不能使用

为了能够在事后调试测试,我还需要按照输出的顺序查看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的内容。对所有这些工作有什么建议吗?

请参阅。吃蛋糕也不容易

从该页:

但有些人既不接受标准输出和标准输出之间失去分离,也不接受线路失步。他们是纯粹主义者,所以他们要求的是最困难的形式——我想将stdout和stderr一起记录到一个文件中,但我也希望他们保持其原始的、单独的目的地

为了做到这一点,我们首先要做一些笔记:

如果将有两个独立的stdout和stderr流,那么一些进程必须写入它们中的每一个。 没有办法在shell脚本中编写一个进程,只要其中一个FD有可用的输入,就从两个单独的FD中读取,因为shell没有poll2或select2接口。 因此,我们需要两个独立的编写器进程。 防止两个独立写入程序的输出互相破坏的唯一方法是确保它们都以追加模式打开输出。在追加模式下打开的FD具有保证属性,即每次向其写入数据时,它都会首先跳到末尾。 因此:

这样可以确保日志文件是正确的。它不能保证写入程序在下一个shell提示符之前完成:


另外,请参见和。

这听起来像是multitee的任务

要在不使用进程替换的情况下测试同步输出或磁盘写入,您可能需要使用Bash shell重定向技巧和类似于以下内容的tee1:

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
有关更多信息,请参阅:

要获取命令的退出代码,除了最后一个退出代码变量$?,您可能还需要分别签出pipefail或PIPESTATUS:

help set | grep -i pipefail
man bash | less -p pipefail
man bash | less -p PIPESTATUS

对于最后一次迭代,如果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"

PIPESTATUS似乎不起作用-如果echos命令返回1,它仍然只是两个零。假设我们谈论的是基于行的字符串,我们可以通过管道(例如,sed)将两个流都传输到行的开头添加标记吗?然后,我们可以从日志文件中判断哪一行来自哪个流。@Raphael:您应该能够在每个进程替换中位于tee之前,例如:exec>>sed's/^/from STDOUT://'| tee-a mylog 2>>sed's/^/from STDERR://'| tee-a mylog>&2未测试。顺便说一句,这会导致终端的输出也带有这些前缀。酷!这难道不会使你的部分报价无效吗?现在我们有了一个同步化的共享输出文件,但仍然知道哪个是哪个,或者我遗漏了什么?@Raphael:对不起,我不知道什么是无效的。也许我误解了第一段,认为这是不可能的。
help set | grep -i pipefail
man bash | less -p pipefail
man bash | less -p PIPESTATUS
# 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"