bash 在shell脚本中将stdout和stderr管道传输到两个不同的进程?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9112979/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 01:28:34  来源:igfitidea点击:

pipe stdout and stderr to two different processes in shell script?

bashshellpipeio-redirection

提问by user964970

I've a pipline doing just

我有一个管道正在做

 command1 | command2

So, stdout of command1 goes to command2 , while stderr of command1 go to the terminal (or wherever stdout of the shell is).

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

How can I pipe stderr of command1 to a third process (command3) while stdout is still going to command2 ?

如何command3在 stdout 仍然转到 command2 时将command1 的 stderr 传输到第三个进程 ( ) ?

采纳答案by olibre

Use another file descriptor

使用另一个文件描述符

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

You can use up to 7 other file descriptors: from 3 to 9.
If you want more explanation, please ask, I can explain ;-)

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

Test

测试

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

output:

输出:

b2
a1

Example

例子

Produce two log files:
1. stderronly
2. stderrand stdout

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

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

If commandis echo "stdout"; echo "stderr" >&2then we can test it like that:

如果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

回答by antak

The accepted answer results in the reversing of stdoutand stderr. Here's a method that preserves them (since Googling on that purpose brings up this post):

接受的答案产生的反转stdoutstderr。这是一种保留它们的方法(因为为此目的谷歌搜索带来了这篇文章):

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

Notice:

注意:

  • 3>&-is required to prevent fd 3from being inherited by command. (As this can lead to unexpected results depending on what commanddoes inside.)
  • 3>&-需要防止fd 3command. (因为这可能会导致意外的结果,具体取决于command内部的内容。)

Parts explained:

部分说明:

  1. Outer part first:

    1. 3>&1-- fd 3for { ... }is set to what fd 1was (i.e. stdout)
    2. 1>&2-- fd 1for { ... }is set to what fd 2was (i.e. stderr)
    3. | stdout_command-- fd 1(was stdout) is piped through stdout_command
  2. Inner part inherits file descriptors from the outer part:

    1. 2>&1-- fd 2for commandis set to what fd 1was (i.e. stderras per outer part)
    2. 1>&3-- fd 1for commandis set to what fd 3was (i.e. stdoutas per outer part)
    3. 3>&--- fd 3for commandis set to nothing (i.e. closed)
    4. | stderr_command-- fd 1(was stderr) is piped through stderr_command
  1. 首先是外部:

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

    1. 2>&1-- fd 2forcommand设置为fd 1是什么(即stderr根据外部)
    2. 1>&3-- fd 1forcommand设置为fd 3是什么(即stdout根据外部)
    3. 3>&--- fd 3forcommand设置为空(即关闭
    4. | stderr_command-- fd 1(was stderr) 是通过管道传输的stderr_command

Example:

例子:

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: /'

Output:

输出:

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

(Order of a -> cand b -> dwill always be indeterminate because there's no form of synchronization between stderr_commandand stdout_command.)

a -> cand 的顺序b -> d总是不确定的,因为stderr_command和之间没有同步形式stdout_command。)

回答by olibre

Simply redirect stderr to stdout

只需将 stderr 重定向到 stdout

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

Caution:commnd3will also read command2stdout (if any).
To avoid that, you can discard commnd2stdout:

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

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

However, to keep command2stdout (e.g. in the terminal),
then please refer to my other answer more complex.

但是,为了保持command2标准输出(例如在终端中),
请参阅我的其他更复杂的答案。

Test

测试

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

output:

输出:

a2
b2
c2
----12

回答by FuePi

Using process substitution:

使用进程替换:

command1 > >(command2) 2> >(command3)

See http://tldp.org/LDP/abs/html/process-sub.htmlfor more info.

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

回答by jbyler

Zsh Version

Zsh版本

I like the answerposted by @antak, but it doesn't work correctly in zsh due to multios. Here is a small tweak to use it in zsh:

我喜欢@antak 发布的答案,但由于 multios,它在 zsh 中无法正常工作。这是在 zsh 中使用它的一个小调整:

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

To use, replace commandwith the command you want to run, and replace stderr_commandand stdout_commandwith your desired pipelines. For example, the command ls / /foowill produce both stdout output and stderr output, so we can use it as a test case. To save the stdout to a file called stdout and the stderr to a file called stderr, you can do this:

为了使用,更换command你想要运行的命令,并更换stderr_commandstdout_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

See @antak's original answer for full explanation.

有关完整解释,请参阅@antak 的原始答案。

回答by FatalError

The same effect can be accomplished fairly easily with a fifo. I'm not aware of a direct piping syntax for doing it (though it would be nifty to see one). This is how you might do it with a fifo.

使用 fifo 可以很容易地实现相同的效果。我不知道这样做的直接管道语法(尽管看到它会很漂亮)。这就是您可以使用 fifo 执行此操作的方式。

First, something that prints to both stdoutand stderr, outerr.sh:

首先,打印到stdoutstderr, 的东西outerr.sh

#!/bin/bash

echo "This goes to stdout"
echo "This goes to stderr" >&2

Then we can do something like this:

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

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

That way you set up the listener for stderroutput first and it blocks until it has a writer, which happens in the next command, using the syntax 2>err. You can see that each wc -cgot 20 characters of input.

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

Don't forget to clean up the fifo after you're done if you don't want it to hang around (i.e. rm). If the other command wants input on stdinand not a file arg, you can use input redirection like wc -c < errtoo.

如果您不想让它闲逛(即rm),请不要忘记在完成后清理 fifo 。如果另一个命令需要输入stdin而不是文件 arg,您也可以使用输入重定向wc -c < err

回答by iBug

Pipe stdout as usual, but use Bash process substitution for the stderr redirection:

像往常一样管道 stdout,但使用 Bash 进程替换来进行 stderr 重定向:

some_command 2> >(command of stderr) | command of stdout

Header: #!/bin/bash

标题: #!/bin/bash