pipe道标准输出和标准错误到两个不同的进程在shell脚本?

我有一个pipline正在做

command1 | command2 

所以,命令1的标准输出到命令2,而命令1的标准input到terminal(或shell的标准输出)。

如何将标准输出stderr的command3到第三个进程( command3 ),而标准输出仍然是命令2?

使用另一个文件描述符

 { command1 2>&3 | command2; } 3>&1 1>&2 | command3 

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

testing

 { { echo a; echo >&2 b; } 2>&3 | sed >&2 's/$/1/'; } 3>&1 1>&2 | sed 's/$/2/' 

输出:

 b2 a1 

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

 { { { command 2>&1 1>&3; } | tee err-only.log; } 3>&1; } > err-and-stdout.log 

如果commandecho "stdout"; echo "stderr" >&2 echo "stdout"; echo "stderr" >&2然后我们可以像这样testing它:

 $ { { { 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 

接受的答案导致stdoutstderr 。 这里有一个方法,保留它们(因为谷歌search引起了这个post):

 { command 2>&1 1>&3 3>&- | stderr_command; } 3>&1 1>&2 | stdout_command 

注意:

  • 3>&-是防止fd 3commandinheritance所必需的。 (因为这可能会导致意外的结果取决于内部的command 。)

部分解释:

  1. 外部第一:

    1. 3>&1fd 3 for { ... }被设置为fd 1 (即stdout
    2. 1>&2fd 1 for { ... }被设置为fd 2的值 (即stderr
    3. | stdout_command | stdout_commandfd 1stdout )通过stdout_command传送
  2. 内部部分从外部inheritance文件描述符:

    1. 2>&1fd 2 for command被设置为fd 1的值 (即每个外部的stderr
    2. 1>&3fd 1 for command设置为fd 3 (即每个外部的stdout
    3. 3>&-fd 3 for command设置为空(即closures
    4. | stderr_command | stderr_commandfd 1stderr )通过stderr_command进行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: /' 

输出:

 out: a err: b err: d out: c 

a -> cb -> d顺序总是不确定的,因为stderr_commandstdout_command之间没有任何同步。)

只需将stderrredirect到标准输出

 { command1 | command2; } 2>&1 | command3 

警告: commnd3也会读取command2 stdout(如果有的话)。
为了避免这种情况,可以丢弃commnd2 stdout:

 { command1 | command2 >/dev/null; } 2>&1 | command3 

但是,要保持command2标准输出(例如在terminal中),
那么请参考我的其他答案比较复杂。

testing

 { { echo -e "a\nb\nc" >&2; echo "----"; } | sed 's/$/1/'; } 2>&1 | sed 's/$/2/' 

输出:

 a2 b2 c2 ----12 

用fifo可以很容易地完成同样的效果。 我不知道这样做的直接pipe道语法(虽然它会很漂亮,看到一个)。 这就是你如何使用fifo来完成的。

首先,打印到stdoutstderrouterr.sh

 #!/bin/bash echo "This goes to stdout" echo "This goes to stderr" >&2 

然后我们可以做这样的事情:

 $ mkfifo err $ wc -c err & [1] 2546 $ ./outerr.sh 2>err | wc -c 20 20 err [1]+ Done wc -c err 

这样,您首先设置stderr输出的侦听器,并使用语法2>err阻塞,直到它具有一个写入器(发生在下一个命令中)。 你可以看到每个wc -c都有20个字符的input。

不要忘记在完成后清理FIFO,如果你不想它挂在(即rm )。 如果另一个命令需要inputstdin而不是arg文件,则可以使用inputredirect,如wc -c < err