我只是做了一个pipline
command1 | command2
因此,command1的stdout转到command2,而command1的stderr转到终端(或shell的stdout所在的地方)。
如果stdout仍然要命令2,我如何将command1的stderr传递给第三个进程(command3
)?
答案 0 :(得分:53)
{ command1 2>&3 | command2; } 3>&1 1>&2 | command3
您最多可以使用7个其他文件描述符:从3到9 如果您想要更多解释,请询问,我可以解释一下; - )
{ { echo a; echo >&2 b; } 2>&3 | sed >&2 's/$/1/'; } 3>&1 1>&2 | sed 's/$/2/'
输出:
b2
a1
生成两个日志文件:
1. stderr
只有
2. stderr
和stdout
{ { { command 2>&1 1>&3; } | tee err-only.log; } 3>&1; } > err-and-stdout.log
如果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
答案 1 :(得分:25)
接受的答案会导致stdout
和stderr
的逆转。这是一种保留它们的方法(因为谷歌搜索就是为了这个目的):
{ command 2>&1 1>&3 3>&- | stderr_command; } 3>&1 1>&2 | stdout_command
注意:
3>&-
需要阻止 fd 3 被command
继承。 (因为这会导致意外结果,具体取决于command
内部的内容。)首先是外部部分:
3>&1
- fd 3 { ... }
设置为 fd 1 (即stdout
)1>&2
- { ... }
的 fd 1 设置为 fd 2 (即stderr
)| stdout_command
- fd 1 (stdout
)通过stdout_command
管道传输内部部分从外部继承文件描述符:
2>&1
- fd 2 command
设置为 fd 1 (即stderr
外部)1>&3
- command
的 fd 1 设置为 fd 3 (即外部stdout
)3>&-
- command
的 fd 3 设置为空(即已关闭)| stderr_command
- fd 1 (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: /'
out: a
err: b
err: d
out: c
(a -> c
和b -> d
的顺序始终是不确定的,因为stderr_command
和stdout_command
之间没有任何形式的同步。)
答案 2 :(得分:13)
{ command1 | command2; } 2>&1 | command3
警告: commnd3
还会读取command2
标准输出(如果有)。
为避免这种情况,您可以放弃commnd2
标准输出:
{ command1 | command2 >/dev/null; } 2>&1 | command3
但是,为了保持command2
标准输出(例如在终端中),
那么请参考我的其他答案更复杂。
{ { echo -e "a\nb\nc" >&2; echo "----"; } | sed 's/$/1/'; } 2>&1 | sed 's/$/2/'
输出:
a2
b2
c2
----12
答案 3 :(得分:5)
使用流程替换:
command1 > >(command2) 2> >(command3)
答案 4 :(得分:2)
我喜欢@antak发布的answer,但是由于multios,它在zsh中无法正常工作。这是在zsh中使用它的一个小调整:
{ unsetopt multios; command 2>&1 1>&3 3>&- | stderr_command; } 3>&1 1>&2 | stdout_command
要使用,请将command
替换为您要运行的命令,并将stderr_command
和stdout_command
替换为所需的管道。例如,命令ls / /foo
将同时生成stdout输出和stderr输出,因此我们可以将其用作测试用例。要将stdout保存到名为stdout的文件中,并将stderr保存到名为stderr的文件中,可以执行以下操作:
{ unsetopt multios; ls / /foo 2>&1 1>&3 3>&- | cat >stderr; } 3>&1 1>&2 | cat >stdout
有关完整说明,请参见@antak的原始答案。
答案 5 :(得分:1)
使用fifo可以相当容易地完成相同的效果。我不知道这样做的直接管道语法(虽然看到一个是很好的)。这就是你如何使用fifo。
首先,打印到stdout
和stderr
,outerr.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个输入字符。
如果您不希望它在四处闲逛(即rm
),请不要忘记在完成后清理fifo。如果另一个命令想要输入stdin
而不是文件arg,则可以使用输入重定向,如wc -c < err
。
答案 6 :(得分:0)
照常使用管道标准输出,但是使用Bash进程替代标准错误重定向:
some_command 2> >(command of stderr) | command of stdout
标题:#!/bin/bash