管道stdout和stderr到shell脚本中的两个不同进程?

use*_*970 54 bash shell pipe io-redirection

我做了一个pipline

 command1 | command2
Run Code Online (Sandbox Code Playgroud)

因此,command1的stdout进入command2,而command1的stderr进入终端(或shell的stdout所在的地方).

command3当stdout仍然要命令2时,如何将command1的stderr传递给第三个进程()?

oli*_*bre 53

使用其他文件描述符

{ command1 2>&3 | command2; } 3>&1 1>&2 | command3
Run Code Online (Sandbox Code Playgroud)

您最多可以使用7个其他文件描述符:从3到9.
如果您想要更多解释,请询问,我可以解释;-)

测试

{ { echo a; echo >&2 b; } 2>&3 | sed >&2 's/$/1/'; } 3>&1 1>&2 | sed 's/$/2/'
Run Code Online (Sandbox Code Playgroud)

输出:

b2
a1
Run Code Online (Sandbox Code Playgroud)

生成两个日志文件:
1.stderr
2. stderrstdout

{ { { command 2>&1 1>&3; } | tee err-only.log; } 3>&1; } > err-and-stdout.log
Run Code Online (Sandbox Code Playgroud)

如果command是,echo "stdout"; echo "stderr" >&2那么我们可以这样测试:

$ { { { echo out>&3;echo err>&1;}| tee err-only.log;} 3>&1;} > err-and-stdout.log
$ head err-only.log err-and-stdout.log
==> err-only.log <==
err

==> err-and-stdout.log <==
out
err
Run Code Online (Sandbox Code Playgroud)

  • 下面antak的回答更完整。它仍然保持 stdout 和 stderr 之间的原始分离,因为该命令没有所有管道。请注意,对于管道,命令在子进程中运行。如果您不希望那样,因为您可能希望命令修改全局变量,则需要创建 fifo 并改用重定向。 (3认同)
  • 在这里找到答案:http://unix.stackexchange.com/questions/18899/when-would-you-use-an-additional-file-descriptor (2认同)

ant*_*tak 30

接受答案的结果的反转stdoutstderr.这是一个保留它们的方法(因为谷歌搜索就是为了这个目的):

{ command 2>&1 1>&3 3>&- | stderr_command; } 3>&1 1>&2 | stdout_command
Run Code Online (Sandbox Code Playgroud)

注意:

  • 3>&-需要防止fd 3被继承command.(因为这会导致意想不到的结果,具体取决于command内部的内容.)

零件说明:

  1. 外部第一部分:

    1. 3>&1- fd 3 for { ... }设置为fd 1(即stdout)
    2. 1>&2- fd 1 for { ... }设置为fd 2(即stderr)
    3. | stdout_command- fd 1(was stdout)通过管道输送stdout_command
  2. 内部部分从外部继承文件描述符:

    1. 2>&1- fd 2 for command设置为fd 1(即stderr根据外部部分)
    2. 1>&3- fd 1 for command设置为fd 3(即stdout根据外部部分)
    3. 3>&-- fd 3 for command设置为空(即关闭)
    4. | stderr_command- fd 1(was stderr)通过管道输送stderr_command

例:

foo() {
    echo a
    echo b >&2
    echo c
    echo d >&2
}

{ foo 2>&1 1>&3 3>&- | sed -u 's/^/err: /'; } 3>&1 1>&2 | sed -u 's/^/out: /'
Run Code Online (Sandbox Code Playgroud)

输出:

out: a
err: b
err: d
out: c
Run Code Online (Sandbox Code Playgroud)

(顺序a -> cb -> d将永远是不确定的,因为stderr_command和之间没有任何形式的同步stdout_command.)


oli*_*bre 14

只需将stderr重定向到stdout即可

{ command1 | command2; } 2>&1 | command3
Run Code Online (Sandbox Code Playgroud)

注意: commnd3还会读取command2标准输出(如果有的话).
为避免这种情况,您可以丢弃commnd2标准输出:

{ command1 | command2 >/dev/null; } 2>&1 | command3
Run Code Online (Sandbox Code Playgroud)

但是,要保持command2stdout(例如在终端),
那么请参考我的其他答案更复杂.

测试

{ { echo -e "a\nb\nc" >&2; echo "----"; } | sed 's/$/1/'; } 2>&1 | sed 's/$/2/'
Run Code Online (Sandbox Code Playgroud)

输出:

a2
b2
c2
----12
Run Code Online (Sandbox Code Playgroud)


Fue*_*ePi 9

使用流程替换:

command1 > >(command2) 2> >(command3)
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅http://tldp.org/LDP/abs/html/process-sub.html.

  • 它也比 POSIX 解决方案好得多。 (5认同)
  • 注意:这不是POSIX,而是一种bashism。 (2认同)