重定向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中执行此操作?
答案 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_PRELOAD
或gdb
黑客,
答案 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)