tee stdout和stderr将文件分开,同时将它们保留在各自的流中

时间:2014-01-30 19:03:44

标签: bash redirect tee

我正在尝试编写一个脚本,它本质上充当由(非交互式)命令创建的所有输出的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)组合到第二个发球台之前将其组合在一起,但如果是这种情况我会期望看到stdout和tee2.txt文件中的stderr,我没有 - 我只看到stderr!在第二个命令的情况下,我从阅读我为此命令调整的答案的印象是描述符被交换以避免这个问题,但显然仍然存在问题。

编辑:我有另一个想法,也许第二个命令失败,因为我正在重定向1>&2,这是从第一个发球台杀死stdout。所以我尝试用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

1 个答案:

答案 0 :(得分:11)

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

{ 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,而标准输出重定向到内部{ {1}}。