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

发布于 2025-01-01 16:50:05 字数 250 浏览 0 评论 0原文

我有一个管道正在这样做

 command1 | command2

,所以, command1 的 stdout 转到 command2 ,而 command1 的 stderr 转到终端(或 shell 的 stdout 所在的任何地方)。

如何将 command1 的 stderr 传送到第三个进程 (command3),同时 stdout 仍传送到 command2 ?

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).

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

三生殊途 2025-01-08 16:50:05

使用另一个文件描述符

{ 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. stderrstdout

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

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

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 ;-)

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. stderr only
2. stderr and stdout

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

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

$ { { { 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
零度℉ 2025-01-08 16:50:05

接受的答案会导致 stdoutstderr 颠倒。这里有一个保留它们的方法(因为谷歌搜索这个目的会带来这篇文章):

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

注意:

  • 需要 3>&- 来防止 fd 3 被 < 继承代码>命令。 (因为这可能会导致意外结果,具体取决于 command 在内部执行的操作。)

部分解释:

  1. 首先是外部部分:

    1. 3>&1 -- { ... }fd 3 设置为 fd 1 em> 是(即 stdout
    2. 1>&2 -- { ... }fd 1 设置为 fd 2 em> 是(即 stderr
    3. <代码>| stdout_command -- fd 1(原为stdout)通过stdout_command传送
  2. 内部部分从外部部分继承文件描述符:

    1. 2>&1 -- commandfd 2 设置为 fd 1 的值 (即 stderr 根据外部部分)
    2. 1>&3 -- commandfd 1 设置为 fd 3 的值 (即 stdout 根据外部部分)
    3. 3>&- -- commandfd 3 设置为空(即关闭
    4. <代码>| 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 的顺序 - > cb -> d 始终是不确定的,因为 stderr_commandstdout_command。)

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

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

Notice:

  • 3>&- is required to prevent fd 3 from being inherited by command. (As this can lead to unexpected results depending on what command does inside.)

Parts explained:

  1. Outer part first:

    1. 3>&1 -- fd 3 for { ... } is set to what fd 1 was (i.e. stdout)
    2. 1>&2 -- fd 1 for { ... } is set to what fd 2 was (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 2 for command is set to what fd 1 was (i.e. stderr as per outer part)
    2. 1>&3 -- fd 1 for command is set to what fd 3 was (i.e. stdout as per outer part)
    3. 3>&- -- fd 3 for command is set to nothing (i.e. closed)
    4. | stderr_command -- fd 1 (was stderr) is piped through 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 -> c and b -> d will always be indeterminate because there's no form of synchronization between stderr_command and stdout_command.)

雪若未夕 2025-01-08 16:50:05

使用进程替换:

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

请参阅 http://tldp.org/LDP/abs/html/ process-sub.html 了解更多信息。

Using process substitution:

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

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

看春风乍起 2025-01-08 16:50:05

只需将 stderr 重定向到 stdout

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

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

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

但是,要保留 command2 stdout(例如在终端中),
那么请参考我另一个更复杂的答案。

测试

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

输出:

a2
b2
c2
----12

Simply redirect stderr to stdout

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

Caution: commnd3 will also read command2 stdout (if any).
To avoid that, you can discard commnd2 stdout:

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

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

Test

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

output:

a2
b2
c2
----12
万劫不复 2025-01-08 16:50:05

Zsh 版本

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

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

要使用它,请将 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

请参阅 @antak 的原始答案以获取完整说明。

Zsh Version

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

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

To use, replace command with the command you want to run, and replace stderr_command and stdout_command with your desired pipelines. For example, the command ls / /foo will 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:

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

过潦 2025-01-08 16:50:05

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

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

标头:#!/bin/bash

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

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

Header: #!/bin/bash

勿忘初心 2025-01-08 16:50:05

使用 fifo 可以相当容易地实现相同的效果。我不知道执行此操作的直接管道语法(尽管看到一个会很漂亮)。这就是使用 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 个输入字符。

如果您不想让 fifo 继续存在(即 rm),请不要忘记在完成后清理 fifo。如果另一个命令需要在 stdin 上输入而不是文件参数,您可以使用输入重定向,例如 wc -c wc -c wc -c wc -c wc -c 也犯错误。

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.

First, something that prints to both stdout and stderr, 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 stderr output 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 -c got 20 characters of input.

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 stdin and not a file arg, you can use input redirection like wc -c < err too.

无尽的现实 2025-01-08 16:50:05

已经过去很长一段时间了,但是...

@oHo 的答案有一个缺点,即将 command2 输出重定向到 stderr。虽然@antak的答案可能会颠倒输出的顺序。

下面的解决方案可能会通过将 command2command3 输出和错误分别正确重定向到 stdoutstderr< 来解决这些问题/code>,如预期并保持顺序。

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

当然,它也满足了OP将输出和错误从command1分别重定向到command2command3的需要。

It's been a long time but...

@oHo's answer has the disadvantage of redirecting command2 outputs to stderr. While @antak's answer may reverse the order of the outputs.

The solution below is likely to fix these problems by correctly redirecting command2 and command3 outputs and errors to, respectively, stdout and stderr, as expected and preserving order.

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

Of course, it also satisfies the OP's need to redirect output and errors from command1 to, respectively, command2 and command3.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文