获取合并和分离的 stdout 和 stderr

Ang*_* SO 5 linux bash redirection

我正在寻找一种运行程序Prog并最终得到 3 个文件的方法:

  1. 标准输出为Prog
  2. 标准错误Prog
  3. Prog如果没有发生重定向,stdout 和 stderr 的组合就像它们在屏幕上一样。

是否有重定向、管道等的组合可以实现这一目标?

注意:我通常使用bash。

ter*_*don 4

据我所知,没有优雅的方法可以做到这一点(对于一种有效但不漂亮的不优雅的方法,请向下滚动到我的答案的末尾)。我怀疑你能做得比以下更好:

\n\n
$ command >stdout.txt 2>stderr.txt && cat stdout.txt stderr.txt > both.txt\n
Run Code Online (Sandbox Code Playgroud)\n\n

您可以使用各种 很酷 技巧,但似乎没有一个能够比上述更好地成功生成 3 个文件。主要问题是该文件both.txt不会以正确的顺序显示消息(STDERR 和 STDOUT)。这是因为(如此处所解释的

\n\n
\n

当您将标准输出和标准错误重定向到同一个文件时,您可能会得到一些意外的结果。这是因为 STDOUT 是缓冲流,而 STDERR 始终是无缓冲的。这意味着 STDERR 的每个字符一旦可用就会被写入,而 STDOUT 会批量写入内容。当 STDOUT 和 STDERR 都访问同一个文件时,您可能会看到错误消息的出现时间早于您对程序或脚本实际输出的预期。\xe2\x80\x99 没有什么值得警惕的\n,而只是缓冲流与非缓冲流的副作用,\n 你只需要记住它即可。

\n
\n\n

我能找到的最好的替代方案是使用 bash 子 shell,它有点复杂,并且仍然不能以正确的顺序显示输出。我制作了一个简单的 Perl 脚本,test.pl将“OUT”打印到STDOUT,将“ERR”打印到STDERR,重复该过程 3 次:

\n\n
#/usr/bin/perl \nfor($i=0; $i<=2; $i++){\n    print STDOUT "OUT\\n"; \n    print STDERR "ERR\\n"\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

其正常的、未重定向的输出是:

\n\n
$ ./test.pl\nOUT\nERR\nOUT\nERR\nOUT\nERR\n
Run Code Online (Sandbox Code Playgroud)\n\n

为了重定向输出,我运行了:

\n\n
(./test.pl 2> >(tee error.txt) > >(tee out.txt)) > both.txt \n
Run Code Online (Sandbox Code Playgroud)\n\n

它使用,一个将其输入打印到屏幕文件名的tee程序。因此,我将其重定向并将其作为输入传递给,告诉它将其写入文件。与和 文件类似。我将整个内容放入子 shell ( ) 中,这样我就可以捕获其所有输出并重定向到.STDERRteeerror.txtSTDOUTout.txt(...)both.txt

\n\n

现在,它可以工作,因为它创建了 3 个文件,一个包含STDERR,一个包含STDOUT,一个两者都包含。但是,如上所述,这会导致消息以错误的顺序出现在both.txt

\n\n
$ cat both.txt \nERR\nERR\nERR\nOUT\nOUT\nOUT\n
Run Code Online (Sandbox Code Playgroud)\n\n

我能找到的唯一方法是将其打印的时间附加到每行输出中,然后进行排序,但它变得非常复杂,在你的位置,我会问自己是否真的值得:

\n\n
 $(./test.pl \\\n   2> >(while read n; do echo `date +%N`" $n"; echo "$n" >>error.txt; done) \\\n    > >(while read n; do echo `date +%N`" $n"; echo "$n" >> out.txt; done )) \\\n | gawk \'{print $2}\'> both.txt \n
Run Code Online (Sandbox Code Playgroud)\n