编辑:我看到我出轨了,并最终回答了一个与被问到的问题不同的问题。真正问题的答案在保罗·汤姆林的答案的底部。(如果您出于某种原因想要增强该解决方案以分别重定向stdout和stderr,则可以使用我在此处描述的技术。)
我一直想要一个保留stdout和stderr之间区别的答案。不幸的是,到目前为止给出的所有保留这种区别的答案都是容易种族化的:正如我在评论中指出的那样,它们会使程序看到输入不完整的风险。
我想我终于找到了一个保留区别的答案,也不是容易种族歧视的,也不是非常麻烦的。
第一个构建块:交换标准输出和标准错误:
my_command 3>&1 1>&2 2>&3-
第二个构建块:如果我们只想过滤(例如tee)stderr,我们可以通过交换stdout&stderr,过滤,然后交换回来来实现:
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
现在剩下的事情很容易了:我们可以在开头添加一个stdout过滤器:
{ { my_command | stdout_filter;} 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3-
或最后:
{ my_command 3>&1 1>&2 2>&3- | stderr_filter;} 3>&1 1>&2 2>&3- | stdout_filter
为了使自己相信以上两个命令都有效,我使用了以下命令:
alias my_command='{ echo "to stdout"; echo "to stderr" >&2;}'
alias stdout_filter='{ sleep 1; sed -u "s/^/teed stdout: /" | tee stdout.txt;}'
alias stderr_filter='{ sleep 2; sed -u "s/^/teed stderr: /" | tee stderr.txt;}'
输出为:
...(1 second pause)...
teed stdout: to stdout
...(another 1 second pause)...
teed stderr: to stderr
并且在“teed stderr: to stderr
预期 ”。
关于zsh的脚注:
上面的解决方案可以在bash中运行(我不确定,也许还有其他一些shell),但是在zsh中不起作用。它在zsh中失败的原因有两个:
2>&3-
zsh无法理解该语法;必须重写为2>&3 3>&-
- 在zsh中(与其他Shell不同),如果您重定向已经打开的文件描述符,则在某些情况下(我不完全了解它的决定),它会执行内置的类似tee的行为。为避免这种情况,您必须在重定向每个fd之前将其关闭。
因此,例如,我的第二个解决方案必须针对zsh进行重写{my_command 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stderr_filter;} 3>&1 1>&- 1>&2 2>&- 2>&3 3>&- | stdout_filter
(也可以在bash中使用,但非常冗长)。
另一方面,您可以利用zsh神秘的内置隐式teeing来获得zsh的更短解决方案,而后者根本不会运行tee:
my_command >&1 >stdout.txt 2>&2 2>stderr.txt
(我不会从文档中猜到我发现the >&1
和2>&2
是触发zsh隐式发球的事物;我通过反复试验发现了这一点。)