如何将stdout + stderr重定向到一个文件,同时保持流分离?

时间:2012-09-20 17:10:58

标签: bash shell unix zsh tee

重定向stdout + stderr,以便在仍然输出到stdout时写入文件都很简单:

cmd 2>&1 | tee output_file

但是现在来自cmd的stdout / stderr都来了stdout。我想将stdout + stderr写入同一个文件(因此,假设cmd是单线程的,则保留排序)但是仍然可以单独重定向它们,如下所示:

some_magic_tee_variant combined_output cmd > >(command-expecting-stdout) 2> >(command-expecting-stderr)

因此,combined_output包含保留顺序的两者,但命令期望-stdout仅获取stdout,而命令期望-stderr仅获取stderr。基本上,我想记录stdout + stderr,同时仍然允许stdout和stderr分别重定向和管道。发球方法的问题在于它们将它们融合在一起。有没有办法在bash / zsh中执行此操作?

5 个答案:

答案 0 :(得分:1)

根据我的意思,这就是你要找的东西。首先,我在stdout和stderr上写了一个小写的脚本。它看起来像这样:

$ cat foo.sh 
#!/bin/bash

echo foo 1>&2
echo bar

然后我就这样跑了:

$ ./foo.sh 2> >(tee stderr | tee -a combined) 1> >(tee stdout | tee -a combined)
foo
bar

我的bash中的结果如下所示:

$ cat stderr
foo
$ cat stdout 
bar
$ cat combined 
foo
bar

请注意,-a标志是必需的,因此tee不会覆盖其他tee的内容。

答案 1 :(得分:1)

{ { cmd | tee out >&3; } 2>&1 | tee err >&2; } 3>&1

或者,要迂腐:

{ { cmd 3>&- | tee out >&3 2> /dev/null; } 2>&1 | tee err >&2 3>&- 2> /dev/null; } 3>&1

请注意,尝试保留订单是徒劳的。这基本上是不可能的。唯一的解决方案是修改“cmd”或使用某些LD_PRELOADgdb黑客,

答案 2 :(得分:1)

确实可以保留订单。下面是一个示例,它按标准输出和错误的生成顺序捕获日志文件,同时在您喜欢的任何终端屏幕上仅显示标准错误。调整以满足您的需求。

1.打开两个窗口(外壳)

2.创建一些测试文件

touch /tmp/foo /tmp/foo1 /tmp/foo2

3.在window1:

mkfifo /tmp/fifo
</tmp/fifo cat - >/tmp/logfile

4.然后,在window2中:

(ls -l /tmp/foo /tmp/nofile /tmp/foo1 /tmp/nofile /tmp/nofile; echo successful test; ls /tmp/nofile1111) 2>&1 1>/tmp/fifo | tee /tmp/fifo 1>/dev/pts/1

其中/ dev / pts / 1可以是您想要的任何终端显示。子shell按顺序运行一些“ls”和“echo”命令,一些成功(提供stdout),一些失败(提供stderr)以生成混合的输出和错误消息流,以便您可以验证正确的排序日志文件。

答案 3 :(得分:1)

我是这样做的:

exec 3>log ; example_command 2>&1 1>&3 | tee -a log ; exec 3>&-

工作示例

bash$ exec 3>log ; { echo stdout ; echo stderr >&2 ; } 2>&1 1>&3 | \
      tee -a log ; exec 3>&-
stderr
bash$ cat log
stdout
stderr

以下是它的工作原理:

exec 3>log设置文件描述符3以重定向到名为log的文件,直到另行通知为止。

example_command为了使这成为一个有效的例子,我使用了{ echo stdout ; echo stderr >&2 ; }。或者您可以使用ls /tmp doesnotexist来提供输出。

此时需要跳到管道|,因为bash首先执行此操作。管道设置管道并将文件描述符1重定向到此管道。所以现在,STDOUT正在进入管道。

现在我们可以从左到右的解释回到我们接下来的位置:2>&1这表示程序中的错误将转到STDOUT当前指向的位置,即进入我们刚设置的管道

1>&3表示STDOUT被重定向到文件描述符3,我们之前将其设置为输出到log文件。因此命令中的STDOUT只是进入日志文件,而不是终端的STDOUT。

tee -a log从管道获取它的输入(你现在记得它是命令中的错误),并将它输出到STDOUT 并且将它附加到{{1文件。

log关闭文件描述符3。

答案 4 :(得分:0)

Victor Sergienko的评论是对我有用的,将exec添加到它的前面使得这对整个脚本起作用(而不是必须在单个命令之后放置它)

exec 2> >(tee -a output_file >&2) 1> >(tee -a output_file)