我有一条管道正在做
command1 | command2
因此,command1的stdout进入command2,而command1的stderr进入终端(或shell的stdout所在的地方)。
如何在stdout仍要传递给command2的同时,将command1的stderr传递给第三进程(command3
?
{ 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
接受的答案导致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
的command
-fd 2设置为fd 1的值(即,外部的stderr
)]将[1>&3
的command
-fd 1stdout
)]3>&-
-fd 3command
设置为无(即closed)| stderr_command
-fd 1stderr
)通过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
之间没有任何形式的同步。)
{ command1 | command2; } 2>&1 | command3
注意: commnd3
也将读取command2
标准输出(如果有)。为了避免这种情况,您可以舍弃commnd2
stdout:{ 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
使用进程替换:
command1 > >(command2) 2> >(command3)
请参阅http://tldp.org/LDP/abs/html/process-sub.html了解更多信息。
使用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
),请不要忘记在完成后清除它。如果其他命令希望在stdin
上输入而不是文件arg,则也可以使用wc -c < err
这样的输入重定向。
与往常一样使用管道标准输出,但是使用Bash进程替代标准错误重定向:
some_command 2> >(command of stderr) | command of stdout
标题:#!/bin/bash
我喜欢@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的原始答案以获取完整说明。
使用进程替换:
使用fifo可以很容易地实现相同的效果。我不知道执行此操作的直接管道语法(尽管看到一个语法很不错)。这就是您可以使用fifo进行操作的方式。
与往常一样使用管道标准输出,但是使用Bash进程替代标准错误重定向: