如何将stdout和stderr分别管道给不同进程且保留终端显示及异常排查
问题原因
这个问题是Bash重定向的执行顺序和进程替换的文件描述符继承规则共同导致的:
- Bash处理重定向是严格按照从左到右的顺序执行的,你的代码里先完成了
stdout(1)到第一个tee进程的重定向,再处理stderr(2)的重定向。 - 进程替换(
>()包裹的部分)生成的子进程,会继承当前shell已经设置好的所有文件描述符。 - 处理stderr的第二个tee进程,默认会把输入的内容分成两份:一份发给后面的
cat > err.log写入日志,另一份输出到自己的stdout。而此时子进程继承的stdout已经被重定向到了处理stdout的第一个tee的输入链路,所以这部分内容最终会被写入out.log。
你去掉tee之后两个日志正常,是因为此时处理stderr的子进程是cat > err.log,它不会向自己的stdout输出任何内容,就算stdout已经被重定向也不会产生额外的数据流。
修复方案
只需要让处理stderr的tee进程,把要输出到终端的内容重定向到真实的stderr,而不是默认的stdout即可,修改后的代码如下:
#!/usr/bin/env bash set -e function generate_output() { echo This message goes to stderr 1>&2 echo This message goes to stdout } generate_output \ 1> >(tee <&0 >(cat > out.log)) \ 2> >(tee <&0 >(cat > err.log) >&2)
修改点仅为在处理stderr的tee进程末尾添加了>&2,指定tee的输出目标为stderr,不会再流入stdout的处理链路。
内容的提问来源于stack exchange,提问作者Samuel Rossille
相关产品推荐
相关产品推荐

