如何使用文件流复制来传送stdout和stderr?

时间:2019-02-07 19:44:52

标签: bash shell

给出运行“ foo” 的命令,以便将其输出传递到程序“ pout” 中,并将其错误传递到程序“ perr “ , 并且“ pout” 的所有输出都在stdout上,而“ perr” 的所有输出都在标准错误上。使用文件流复制。不要使用命名管道。

我无法弄清楚流复制的工作原理,因此我尝试使用普通管道进行复制。一些有助于理解流复制的技术将非常有用。

( ./foo | ./pout ) 2>&1 | ./perr

2 个答案:

答案 0 :(得分:2)

您应该看看https://wiki.bash-hackers.org/howto/redirection_tutorial。让我们看看这如何适用于您的示例:

让我们提供一些shell函数来进行尝试:

# Some helper to show you what is is going on with the file descriptors:
function lofd { lsof -ad 0,1,2,3,4 -p $BASHPID -w; }
# foo's stdout goes into pout, foo's stderr goes to perr:
function foo { echo foo-out; echo foo-err 1>&2; lofd; }
function pout { sed 's/foo-out/pout-out/'; echo pout-err 1>&2; lofd; }
function perr { sed 's/foo-err/perr-out/'; echo perr-err 1>&2; lofd; }

检查foostderrstdout的输出:

foo 1>out 2>err
grep "" out err

foopout,同时将pout打印到stderrstdout

{ foo | pout; } 1>out 2>err
grep "" out err

确保pout的所有输出都流向stdout

{ foo | pout 2>&1; } 1>out 2>err
grep "" out err

现在,它变得棘手:当我们通过perr通过管道传输(确保来自perr的所有输出都流向stderr)时,我们看到perrpoutstderr中的foo通过:

{ { foo | pout 2>&1; } | perr 1>&2; } 1>out 2>err # not as intended
grep "" out err

我们只能在某些临时文件描述符的帮助下进行管理。一方面,我们将fd 3用作stdout的备份。然后pout可以使用它来将管道“旁路”到perr中:

{ { { foo | pout 1>&3 2>&3; } | perr 1>&2; } 3>&1; } 1>out 2>err
grep "" out err

此外,我们使用fd 4将管道移交到perr中,以便foo可以使用它:

{ { { foo 2>&4 | pout 1>&3 2>&3; } 4>&1 | perr 1>&2; } 3>&1; } 1>out 2>err
grep "" out err

最后,进行一些清理(关闭未使用的文件描述符:查看lsof的输出以了解此处的情况)

{ { { foo 2>&4 3>&- 4>&- | pout 1>&3 2>&3 3>&- 4>&-; } 4>&1 | perr 1>&2 3>&-; } 3>&1; } 1>out 2>err
grep "" out err

没有我们的调试上下文:

{ { foo 2>&4 3>&- 4>&- | pout 1>&3 2>&3 3>&- 4>&-; } 4>&1 | perr 1>&2 3>&-; } 3>&1

瞧!

答案 1 :(得分:1)

使用其他管道(此处为5)

( ( ./foo | ./pout ) 2>&1 1>&5 | ./perr ) 5>&1
  • 2>&1将stderr重定向到stdout
  • 1>&5将标准输出重定向到pipe5
  • ./perr看不到./pout的标准输出
  • 5>&1再次将pipe5重定向到stdout。