使用 tee 进行进程替换和重定向

使用 tee 进行进程替换和重定向

假设您在 bash 中将命令的标准输出重定向cmd到名为 的文件f.out,并将标准错误重定向到f.err,使用tee保留控制台打印:

cmd 1> >(tee f.out) 2> >(tee f.err)

然后f.out包含输出以及错误(至少在我的系统上)。

现在,如果您更改重定向的顺序:

cmd 2> >(tee f.err) 1> >(tee f.out)

f.out仅包含输出(并且f.err仅包含两种情况下的错误)。

所以我的问题是双重的:如何将 stderr 重定向到f.out,以及为什么重定向的顺序会影响结果?

请注意,如果您不使用tee, 但例如cat,如下所示:

cmd 1> >(cat>f.out) 2> >(cat>f.err)

您没有这个问题,并且重定向的顺序并不重要,正如预期的那样,并且在没有进程替换的情况下(cmd 1>f.out 2>f.err)。

答案1

重定向的顺序很重要,因为 Bash 按照在它解释的命令中找到它们的顺序应用它们。

这是故意的,这样您就可以有像> file 2>&1按预期工作那样的习惯用法,即让 stderr 与 stdout 相同。这个惯用语的工作原理与“分配file给 stdout,然后使 stderr 等于 stdout”一样,这会产生预期的结果,因为当 stderr 获得 stdout 的相同值时,stdout 的值是file。另一种方式(即2>&1 1> file)不会产生相同的结果,因为 stdout 的值在复制到 stderr 的值后发生了更改。文件描述符可以被认为类似于常规变量,它们有自己的值,并且可以用来获取另一个变量值的副本,如 中var1="${var2}",并且很像这样var1不会跟随var2的后续值更改,文件描述符的值也不会。

它也很方便,例如您可以在同一行上交换文件描述符,就像在3>&1 1>&2 2>&3-.这使用 fd 3 作为临时“助手”fd 来交换 fd 1 和 2。

因此,您可以将重定向视为按顺序执行的指令,就像它们位于命令或脚本的两个单独行上一样。

根据您的具体情况,还涉及流程替换,并且这些替换也会按指定的顺序执行继承到目前为止表达的重定向

也就是说,总而言之:

  1. 首先将 stdout 重定向到正在运行的进程tee f.out;此时cmd的 stdout 根据需要连接到tee f.out的 stdin
  2. 然后将 stderr 重定向到正在运行的进程tee f.err;但这按照之前表达的重定向继承了它的标准输出,即连接到tee f.out的标准输入

因此tee f.err,通过无害地输出到其 stdout 以及 f.err 文件,将您的cmd错误消息通过管道传输到tee f.outstdin,从而接收所有消息,将它们输出到 f.out 文件以及终端窗口。

答案2

我遇到了这个问题,这是我正在进行的测试的答案的一部分。该命令的一般情况是运行 cmd 并在终端上显示,修改 stderr(缩进并着色为红色),然后将屏幕上显示的内容保存在日志文件中。从日志中删除颜色编码。

因此,创建 2 个 stdout 和 2 个 stderr 行的示例 cmd 是

{ ls -l /usr/bin/col{1,2,a,b} 2>&1 >&3 | sed 's/.*/\t\o33[31mSTDERR: &\o33[0m/'; } 3>&1  

现在,将副本放入日志文件中

{ ls -l /usr/bin/col{1,2,a,b} 2>&1 >&3 | sed 's/.*/\t\o33[31mSTDERR: &\o33[0m/'; } 3>&1 | tee >( sed 's/\o33//g;s/\[31m//g;s/\[0m//' > /tmp/file )

然而,这工作得很好;有更好的方法来实现这一目标吗?

相关内容