在bash中同时重定向标准输出和标准错误输出?

235

看起来新版本的bash有&>运算符,它(如果我理解正确)将标准输出和错误输出都重定向到一个文件中(&>>会将内容添加到文件末尾,就像Adrian澄清的那样)。

实现相同功能但是将结果通过管道传输到另一个命令的最简单方法是什么?

例如,在这行中:

cmd-doesnt-respect-difference-between-stdout-and-stderr | grep -i SomeError

我希望grep命令可以同时匹配标准输出(stdout)和标准错误(stderr)的内容(实际上,将它们合并为一个流)。

注意:此问题询问的是管道传输(pipe),而不是重定向(redirecting) - 因此它不是当前被标记为重复的那个问题的重复。


请查看链接问题的第二个答案(https://dev59.com/BXRB5IYBdhLWcg3wZWfi#637834),了解将stdout和stderr同时重定向的正确方法。无需提出另一个问题。 - Marki555
7
不是完全重复的情况,对吧?管道和重定向到文件有所不同? - Benjamin W.
@BenjaminW 那里至少有一个答案解决了这两种情况,尽管它不是被采纳的答案。这是一个相当常见的问题,因此我们可能可以找到更好的重复,或者要求管理员将这些内容合并 - 或者在最坏的情况下,为此主题制作全新的规范。如果你发现更好的重复内容,请提出建议。先感谢您。 - tripleee
16
是的,@tripleee 给出了解决方案,但是没有答案使用 |& 这个快捷方式,我认为这是将“同时将标准输出和标准错误输出重定向到管道”最方便的解决方案。 - Benjamin W.
3
这不是与已链接的问题重复,而且Marko的答案并没有清楚地解决我的问题。此外,它也没有提到|&。我投票支持重新开放。 - Martin Bonner supports Monica
3个回答

231

请注意,&>>file 追加 文件内容,而&>则会将原有文件覆盖。

要将stdoutstderr结合在一起,您可以使用1>&2将后者重定向到前者。 这将stdout(文件描述符1)重定向到stderr(文件描述符2),例如:

$ { echo "stdout"; echo "stderr" 1>&2; } | grep -v std
stderr
$

stdout输出到标准输出,stderr输出到标准错误。由于grep只能看到stdout,因此stderr会打印到终端。

另一方面:

$ { echo "stdout"; echo "stderr" 1>&2; } 2>&1 | grep -v std
$

在向标准输出和标准错误输出写入内容之后,2>&1 将标准错误输出重定向回标准输出,grep 从标准输入中读取两个字符串并过滤掉它们。

您可以在这里阅读有关重定向的更多信息。

关于您的示例(POSIX):

cmd-doesnt-respect-difference-between-stdout-and-stderr 2>&1 | grep -i SomeError

或者,使用>=bash-4:

cmd-doesnt-respect-difference-between-stdout-and-stderr |& grep -i SomeError

感谢对 &>> 的澄清。我已经更正了我的问题。 - Andrew Ferrier
23
我把你的例子添加到了我的回答中,以防我的示例不够明显。顺带一提,你也可以使用 bash 特有的 |& 替代 2>&1 | - Adrian Frühwirth
17
关于@AdrianFrühwirth提出的快捷方式|&,需要注意的是:此功能仅支持bash 4+版本。如果你使用的是3或更低版本,则必须使用2>&1 | - tomocafe
6
Bash 重定向在这里 有很好的解释。@AdrianFrühwirth 做得很好,链接提供了更深入的内容。有时候,我希望官方的 Bash 文档也能这么好。 - David Andreoletti
1
@AdrianFrühwirth FYI:你的第一个示例在描述中引用了“2>&1”,但实际上在代码中使用了“1&2”。 - JESii
很好的回答@AdrianFrühwirth,谢谢!我想知道您是否可以评论一下我的新问题https://dev59.com/7ngPtIcB2Jgan1znX3F1,这是相关的。使用您在此处的建议,我仍然找不到解决方案。 - Thomson Comer

192

Bash有一个缩写形式|&来表示2>&1 |,它可以将标准输出和标准错误输出一起传输(请参见手册):

cmd-doesnt-respect-difference-between-stdout-and-stderr |& grep -i SomeError

这是在Bash 4.0中引入的,详见发行说明


1
感谢您为完整性添加此内容。 我将保留另一个答案的正确性,因为许多人仍在使用 bash pre-4.0。但这是有用的。 - Andrew Ferrier
15
或许最值得注意的是,内置在 macOS 系统中的 Bash 版本过于陈旧,无法支持此功能。 - Flimm
1
@Flimm 但是zsh不是。 - Trenton
1
由于ksh使用|&来处理coproc,这似乎是一个不必要的缩写选择,因此不太好。我和其他人一样讨厌看到堆积的dups和重定向行,但明确表达意图也是有道理的……我很抱歉这条评论没有多大帮助,只是想表达对这种缩写的厌恶,而不是对一个实际上有用的答案进行downvote,因为人们能够看到这个真的很好。我之前不知道这一点,所以感谢你让我意识到了这一点。 - Paul Hodges
1
@PaulHodges 我同意它不是可移植的 - 我主要喜欢在交互式Bash会话中使用它,以避免输入太多。 - Benjamin W.
显示剩余4条评论

2
如果你想将 STDOUT 和 STDERR 分别传输到不同的子 shell 而不是传输到同一个子 shell(就像 |& / 2>&1 那样),我会使用以下方法:
producer \
  >  >( stdout_pipeline... ) \
  2> >( stderr_pipeline... ) \
  ...

虽然这里使用了重定向运算符,但它在底层使用管道将生产者连接到子shell。

您可以通过这种方式处理多个输出流(2不是极限),并且还可以使用企鹅运算符([n]< <(...))以类似的方式从子shell中传输(可能是多个)输入流。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接