问题 tee stdout和stderr分隔文件,同时将它们保留在各自的流上


我正在尝试编写一个脚本,它本质上充当由(非交互式)命令创建的所有输出的passthru日志,而不会影响命令输出到其他进程。也就是说,stdout和stderr应该看起来像是没有运行我的命令一样。

为此,我尝试将stdout和stderr分别重定向到两个不同的T恤,每个T恤用于不同的文件,然后重新组合它们,使它们仍然分别出现在stdout和stderr上。我已经看到了许多关于发球和重定向的其他问题,并且已经尝试了从那些中收集到的一些答案,但是它们似乎都没有合并将两者分成分开的三通,然后正确地重新组合它们。

我的尝试成功将输出拆分为正确的文件,但实际的stdout / stderr输出没有正确保留流。我在一个更复杂的设置中看到了这一点,所以我创建了简化的命令,我将数据回显到stdout或stderr作为我的“命令”,如下所示。

以下是我尝试过的几件事:

{ command | tee ~/tee.txt; } 2>&1 | { tee ~/tee2.txt 1>&2; }

运行我的简单测试我看到:

$ { { { echo "test" 1>&2; } | tee ~/tee.txt; } 2>&1 | { tee ~/tee2.txt 1>&2; } } > /dev/null
test
$ { { { echo "test" 1>&2; } | tee ~/tee.txt; } 2>&1 | { tee ~/tee2.txt 1>&2; } } 2> /dev/null
$

好的,这正如我所料。我正在回应stderr,所以当我只重定向stdout时,当我将最终的stderr重定向到/ dev / null和我的原始echo时,我希望看不到任何内容。

$ { { { echo "test";  } | tee ~/tee.txt; } 2>&1 | { tee ~/tee2.txt 1>&2; } } > /dev/null
test
$ { { { echo "test";  } | tee ~/tee.txt; } 2>&1 | { tee ~/tee2.txt 1>&2; } } 2> /dev/null
$

这是倒退!我的命令只将数据发送到stdout,所以当我将最终的stdout重定向到null时,我希望看不到任何内容。但事实恰恰相反。

这是我试过的第二个命令,它有点复杂:

{ command 2>&3 | tee ~/tee.txt; } 3>&1 1>&2 | { tee /home/michael/tee2.txt 1>&2; }

不幸的是,我看到了与以前相同的行为。

我无法真正看到我做错了什么,但似乎stdout在某种程度上受到了破坏。在第一个命令的情况下,我怀疑这是因为我正在组合stdout和stderr(2>&1在我把它管道到第二个发球台之前,但是如果是这种情况我会期望在tee2.txt文件中看到stdout和stderr,我没有 - 我只看到stderr!在第二个命令的情况下,我从阅读我为此命令调整的答案的印象是描述符被交换以避免这个问题,但显然有些事情仍然是错误的。

编辑:我有另一个想法,也许第二个命令失败,因为我正在重定向 1>&2 这就是从第一个发球台杀死标准杆。所以我试着用它重定向 1>&4 然后将其重定向到最后的stdout:

{ command 2>&3 | tee ~/tee.txt; } 3>&1 1>&4 | { tee /home/michael/tee2.txt 1>&2 4>&1; }

但现在我得到:

-bash: 4: Bad file descriptor

我还尝试将描述符2重定向到最终发球台中的描述符1:

{ command 2>&3 | tee ~/tee.txt; } 3>&1 1>&2 | { tee /home/michael/tee2.txt 1>&2 2>&1; }

和:

{ command 2>&3 | tee ~/tee.txt; } 3>&1 1>&2 | { tee /home/michael/tee2.txt 1>&2; } 2>&1

12339
2018-01-30 19:03


起源



答案:


基于流程替换的解决方案很简单,但并不像您想象的那么简单。我的第一次尝试似乎应该有效

{ echo stdout; echo stderr >&2; } > >( tee ~/stdout.txt ) \
                                 2> >( tee ~/stderr.txt )

但是,它并没有像预期的那样有效 bash 因为第二个 tee 从原始命令继承其标准输出(因此它转到 第一  tee)而不是来自调用shell。目前尚不清楚这是否应该被认为是一个错误 bash

可以通过将输出重定向分成两个单独的命令来修复它:

{ { echo stdout; echo stderr >&2; } > >(tee stdout.txt ); } \
                                   2> >(tee stderr.txt )

更新:第二个 tee 应该是 tee stderr.txt >&2 这样从标准错误中读取的内容将打印回标准错误。

现在,标准错误的重定向发生在没有重定向其标准输出的命令中,因此它以预期的方式工作。外部复合命令将其标准错误重定向到外部 tee,其标准输出留在终端上。该  复合命令从外部继承其标准错误(因此它也会转到外部 tee,而 它的 标准输出被重定向到内部 tee


11
2018-01-30 19:53



这似乎没有保留原件 stderr 和 stdout 分离;它似乎将它们结合起来。例如,如果我将整个语句包装在中 { ... } > /dev/null 我看不到输出,但是如果我把它包起来的话 { ... } 2> /dev/null 我看到“stdout”和“stderr”。 - Michael
这又是我所期待的...... - Michael
改变第二个发球台 (tee stderr.txt 1&2) 似乎有所期望的效果。 - Michael
假设你的意思 2> >(tee stderr 1>&2),是的,这是我的一个遗漏,以恢复其输出 tee 到标准错误。 - chepner
更简单的版本是否也适用于此更正?我认为更新代码块中的答案并在评论中解释可能会更好,而不是让错误的版本最突出。 - Sam Brightman


答案:


基于流程替换的解决方案很简单,但并不像您想象的那么简单。我的第一次尝试似乎应该有效

{ echo stdout; echo stderr >&2; } > >( tee ~/stdout.txt ) \
                                 2> >( tee ~/stderr.txt )

但是,它并没有像预期的那样有效 bash 因为第二个 tee 从原始命令继承其标准输出(因此它转到 第一  tee)而不是来自调用shell。目前尚不清楚这是否应该被认为是一个错误 bash

可以通过将输出重定向分成两个单独的命令来修复它:

{ { echo stdout; echo stderr >&2; } > >(tee stdout.txt ); } \
                                   2> >(tee stderr.txt )

更新:第二个 tee 应该是 tee stderr.txt >&2 这样从标准错误中读取的内容将打印回标准错误。

现在,标准错误的重定向发生在没有重定向其标准输出的命令中,因此它以预期的方式工作。外部复合命令将其标准错误重定向到外部 tee,其标准输出留在终端上。该  复合命令从外部继承其标准错误(因此它也会转到外部 tee,而 它的 标准输出被重定向到内部 tee


11
2018-01-30 19:53



这似乎没有保留原件 stderr 和 stdout 分离;它似乎将它们结合起来。例如,如果我将整个语句包装在中 { ... } > /dev/null 我看不到输出,但是如果我把它包起来的话 { ... } 2> /dev/null 我看到“stdout”和“stderr”。 - Michael
这又是我所期待的...... - Michael
改变第二个发球台 (tee stderr.txt 1&2) 似乎有所期望的效果。 - Michael
假设你的意思 2> >(tee stderr 1>&2),是的,这是我的一个遗漏,以恢复其输出 tee 到标准错误。 - chepner
更简单的版本是否也适用于此更正?我认为更新代码块中的答案并在评论中解释可能会更好,而不是让错误的版本最突出。 - Sam Brightman