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

Mic*_*ael 12 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; }
Run Code Online (Sandbox Code Playgroud)

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

$ { { { 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
$
Run Code Online (Sandbox Code Playgroud)

好的,这正如我所料.我正在回应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
$
Run Code Online (Sandbox Code Playgroud)

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

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

{ command 2>&3 | tee ~/tee.txt; } 3>&1 1>&2 | { tee /home/michael/tee2.txt 1>&2; }
Run Code Online (Sandbox Code Playgroud)

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

我无法真正看到我做错了什么,但似乎stdout在某种程度上受到了破坏.在第一个命令的情况下,我怀疑这是因为我结合输出和错误(2>&1之前我管的第二发球),但如果是这样的情况下,我希望看到在tee2标准输出和标准错误. txt文件,我没有 - 我只看到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; }
Run Code Online (Sandbox Code Playgroud)

但现在我得到:

-bash: 4: Bad file descriptor
Run Code Online (Sandbox Code Playgroud)

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

{ command 2>&3 | tee ~/tee.txt; } 3>&1 1>&2 | { tee /home/michael/tee2.txt 1>&2 2>&1; }
Run Code Online (Sandbox Code Playgroud)

和:

{ command 2>&3 | tee ~/tee.txt; } 3>&1 1>&2 | { tee /home/michael/tee2.txt 1>&2; } 2>&1
Run Code Online (Sandbox Code Playgroud)

che*_*ner 12

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

{ echo stdout; echo stderr >&2; } > >( tee ~/stdout.txt ) \
                                 2> >( tee ~/stderr.txt )
Run Code Online (Sandbox Code Playgroud)

但是,它并没有按预期工作,bash因为第二个tee从原始命令继承其标准输出(因此它转到第一个 tee)而不是从调用shell 继承.目前尚不清楚这是否应被视为一个错误bash.

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

{ { echo stdout; echo stderr >&2; } > >(tee stdout.txt ); } \
                                   2> >(tee stderr.txt )
Run Code Online (Sandbox Code Playgroud)

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

现在,标准错误的重定向发生在没有重定向其标准输出的命令中,因此它以预期的方式工作.外部复合命令将其标准错误重定向到外部tee,其标准输出保留在终端上.所述化合物命令继承了外(因此它也进入到外其标准误差tee,而标准输出重定向到内tee.

  • 更简单的版本是否也适用于此更正?我认为更新代码块中的答案并在评论中解释可能会更好,而不是让错误的版本最突出. (4认同)