bash 如何在保持流分离的同时将 stdout+stderr 重定向到一个文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12517519/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to redirect stdout+stderr to one file while keeping streams separate?
提问by Joseph Garvin
Redirecting stdout+stderr such that both get written to a file while still outputting to stdout is simple enough:
重定向 stdout+stderr 使得两者都被写入文件同时仍然输出到 stdout 很简单:
cmd 2>&1 | tee output_file
But then now both stdout/stderr from cmd are coming on stdout. I'd like to write stdout+stderr to the same file (so ordering is preserved assuming cmd is single threaded) but then still be able to also separately redirect them, something like this:
但是现在来自 cmd 的 stdout/stderr 都出现在 stdout 上。我想将 stdout+stderr 写入同一个文件(因此假设 cmd 是单线程的,则保留顺序),但仍然可以单独重定向它们,如下所示:
some_magic_tee_variant combined_output cmd > >(command-expecting-stdout) 2> >(command-expecting-stderr)
So combined_output contains the both with order preserved, but the command-expecting-stdout only gets stdout and command-expecting-stderr only gets stderr. Basically, I want to log stdout+stderr while still allowing stdout and stderr to be separately redirected and piped. The problem with the tee approach is it globs them together. Is there a way to do this in bash/zsh?
因此,combined_output 包含两者并保留了顺序,但 command-expecting-stdout 仅获得 stdout,而 command-expecting-stderr 仅获得 stderr。基本上,我想记录 stdout+stderr,同时仍然允许 stdout 和 stderr 被单独重定向和管道传输。T 恤方法的问题在于它将它们连接在一起。有没有办法在 bash/zsh 中做到这一点?
回答by keks
From what I unterstand this is what you are looking for. First I made a litte script to write on stdout and stderr. It looks like this:
据我了解,这就是你要找的。首先,我制作了一个小脚本来编写 stdout 和 stderr。它看起来像这样:
$ cat foo.sh
#!/bin/bash
echo foo 1>&2
echo bar
Then I ran it like this:
然后我像这样运行它:
$ ./foo.sh 2> >(tee stderr | tee -a combined) 1> >(tee stdout | tee -a combined)
foo
bar
The results in my bashlook like this:
结果在我bash看来是这样的:
$ cat stderr
foo
$ cat stdout
bar
$ cat combined
foo
bar
Note that the -a flag is required so the tees don't overwrite the other tee's content.
请注意, -a 标志是必需的,因此tees 不会覆盖其他tee人的内容。
回答by Stephane Chazelas
{ { cmd | tee out >&3; } 2>&1 | tee err >&2; } 3>&1
Or, to be pedantic:
或者,要学究:
{ { cmd 3>&- | tee out >&3 2> /dev/null; } 2>&1 | tee err >&2 3>&- 2> /dev/null; } 3>&1
Note that it's futile to try and preserve order. It is basically impossible. The only solution would be to modify "cmd" or use some LD_PRELOADor gdbhack,
请注意,试图保持秩序是徒劳的。基本上是不可能的。唯一的解决方案是修改“cmd”或使用一些LD_PRELOAD或gdb黑客,
回答by Michael Martinez
Order can indeed be preserved. Here's an example which captures the standard output and error, in the order in which they are generated, to a logfile, while displaying only the standard error on any terminal screen you like. Tweak to suit your needs.
秩序确实可以保留。这是一个示例,它按照生成顺序将标准输出和错误捕获到日志文件中,同时在您喜欢的任何终端屏幕上仅显示标准错误。调整以满足您的需求。
1.Open two windows (shells)
1.打开两个窗口(贝壳)
2.Create some test files
2.创建一些测试文件
touch /tmp/foo /tmp/foo1 /tmp/foo2
3.In window1:
3.在窗口1中:
mkfifo /tmp/fifo
</tmp/fifo cat - >/tmp/logfile
4.Then, in window2:
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
Where /dev/pts/1 can be whatever terminal display you want. The subshell runs some "ls" and "echo" commands in sequence, some succeed (providing stdout) and some fail (providing stderr) in order to generate a mingled stream of output and error messages, so that you can verify the correct ordering in the log file.
其中 /dev/pts/1 可以是您想要的任何终端显示。子shell按顺序运行一些“ls”和“echo”命令,一些成功(提供stdout)和一些失败(提供stderr)以生成输出和错误消息的混合流,以便您可以验证正确的顺序日志文件。
回答by artfulrobot
Here's how I do it:
这是我的方法:
exec 3>log ; example_command 2>&1 1>&3 | tee -a log ; exec 3>&-
Worked Example
工作示例
bash$ exec 3>log ; { echo stdout ; echo stderr >&2 ; } 2>&1 1>&3 | \
tee -a log ; exec 3>&-
stderr
bash$ cat log
stdout
stderr
Here's how that works:
这是它的工作原理:
exec 3>logsets up file descriptor 3 to redirect into the file called log, until further notice.
exec 3>log设置文件描述符 3 以重定向到名为 log 的文件,直至另行通知。
example_commandto make this a working example, I used { echo stdout ; echo stderr >&2 ; }. Or you could use ls /tmp doesnotexistto provide output instead.
example_command为了使它成为一个工作示例,我使用了{ echo stdout ; echo stderr >&2 ; }. 或者您可以使用ls /tmp doesnotexist来提供输出。
Need to jump ahead to the pipe |at this point because bash does it first. The pipe sets up a pipe and redirects the file descriptor 1 into this pipe. So now, STDOUT is going into the pipe.
此时需要跳到管道|,因为 bash 首先执行此操作。管道建立一个管道并将文件描述符 1 重定向到这个管道。所以现在,STDOUT 进入管道。
Now we can go back to where we were next in our left-to-right interpretation: 2>&1this says errors from the program are to go to where STDOUT currently points, i.e. into the pipe we just set up.
现在我们可以回到我们从左到右的解释中的下一个位置:2>&1这表示程序中的错误将转到 STDOUT 当前指向的位置,即进入我们刚刚设置的管道。
1>&3means STDOUT is redirected into file descriptor 3, which we earlier set up to output to the logfile. So STDOUT from the command just goes into the log file, not to the terminal's STDOUT.
1>&3意味着 STDOUT 被重定向到文件描述符 3,我们之前设置为输出到log文件。所以命令中的 STDOUT 只会进入日志文件,而不是终端的 STDOUT。
tee -a logtakes it's input from the pipe (which you'll remember is now the errors from the command), and outputs it to STDOUT and alsoappends it to the logfile.
tee -a log采取它的从管道(你会记得现在是该命令的错误),并将其输出到标准输出输入,并追加到log文件中。
exec 3>&-closes the file descriptor 3.
exec 3>&-3.关闭文件描述符
回答by Marcus
Victor Sergienko's comment is what worked for me, adding exec to the front of it makes this work for the entire script (instead of having to put it after individual commands)
Victor Sergienko 的评论对我有用,在它的前面添加 exec 使整个脚本都可以使用(而不必将它放在单个命令之后)
exec 2> >(tee -a output_file >&2) 1> >(tee -a output_file)
exec 2> >(tee -a output_file >&2) 1> >(tee -a output_file)

