bash exec将输出发送到管道,怎么样?

Har*_*ald 10 bash io-redirection

我试着执行bash本身只是为了重定向输出.如果我使用重定向

exec >bla.log
ls
exec 1>&2
Run Code Online (Sandbox Code Playgroud)

它按预期工作:ls输出bla.log在第二个exec事物恢复正常之后和之后结束,主要是因为句柄2仍然绑定到终端.

现在我想通过管道而不是文件发送输出,这是一个简单的例子exec | cat >bla.log.但是,该命令立即返回.为了弄清楚发生了什么,我这样做了:

exec | bash -c 'echo $$; ls -l /proc/$$/fd /proc/23084/fd'
Run Code Online (Sandbox Code Playgroud)

其中23084是当前运行的bash并得到了:

24002
/proc/23084/fd:
total 0
lrwx------ 1 harald harald 64 Aug 14 20:17 0 -> /dev/pts/1
lrwx------ 1 harald harald 64 Aug 14 20:17 1 -> /dev/pts/1
lrwx------ 1 harald harald 64 Aug 14 20:17 2 -> /dev/pts/1
lrwx------ 1 harald harald 64 Aug 14 20:17 255 -> /dev/pts/1

/proc/24002/fd:
total 0
lr-x------ 1 harald harald 64 Aug 14 21:56 0 -> pipe:[58814]
lrwx------ 1 harald harald 64 Aug 14 21:56 1 -> /dev/pts/1
lrwx------ 1 harald harald 64 Aug 14 21:56 2 -> /dev/pts/1
Run Code Online (Sandbox Code Playgroud)

如我们所见,子过程24002确实正在监听管道.但它肯定不是父流程23084,它有这个管道打开.

有什么想法在这里发生了什么?

Cha*_*ffy 17

什么

实现可能以其他方式编写的内容的正确方法

exec | cat >bla.log
Run Code Online (Sandbox Code Playgroud)

#!/bin/bash
#      ^^^^ - IMPORTANT: not /bin/sh

exec > >(cat >bla.log)
Run Code Online (Sandbox Code Playgroud)

为什么

这是因为>()是一个过程替代 ; 它被替换为文件名(/dev/fd/NN如果可能的话,或者是临时的FIFO),当写入时,将传递给封闭过程的标准输入.(<()类似,但在另一个方向:用类似文件的对象的名称替换,当读取时,将返回给定进程的标准输出).

因此,exec > >(cat >bla.log)大致相当于以下(一个操作系统不提供上/dev/fd,/proc/self/fds或类似的):

mkfifo "tempfifo.$$"           # implicit: FIFO creation
cat >bla.log <"tempfifo.$$" &  # ...start the desired process within it...
exec >"tempfifo.$$"            # explicit: redirect to the FIFO
rm "tempfifo.$$"               # ...and can unlink it immediately.
Run Code Online (Sandbox Code Playgroud)


Bar*_*mar 5

当命令包含管道时,每个子命令都在子shell中运行.所以shell首先为管道的每个部分分配一个子shell,然后第一个部分的子shell执行exec时不带任何参数,它不执行任何操作并退出.

exec使用重定向,没有命令被视为特殊情况.从文档:

如果未指定command,则任何重定向在当前shell中生效,返回状态为0.


Jin*_*nKo 5

我花了一段时间才弄清楚如何为 stdout 和 stderr 正确组合重定向,因此这可能对其他人有用。

以下示例说明了如何使用以 tee 作为“管道”目标的重定向,同时区分 stdout 和 stderr。

#!/bin/bash

echo "stdout"
echo "stderr" >&2

echo "stdout to out.log" | tee out.log
echo "stderr to err.log" 2>&1 >&2 | tee err.log >&2

exec 2> >(tee -a err.log >&2)
exec > >(tee -a out.log)

echo "exec stdout to out.log"
echo "exec stderr to err.log" >&2
Run Code Online (Sandbox Code Playgroud)

在 CLI 中运行此命令,并将 stdout 重定向到 /dev/null,您只能看到 stderr 消息。另外,在每个日志文件中您只能看到相关消息。

exec 2>请注意,行和行的顺序exec >很重要。本质上,我们首先要将 stderr 重定向到错误文件,然后将 stdout 重定向到日志文件。如果这些行以相反的顺序出现,则结果将不正确。

  • 顺便说一句,您可以仅使用一个具有多个重定向的“exec”;它们将按从左到右的顺序执行。因此 `exec 2&gt; &gt;(tee -a err.log &gt;&amp;2) &gt; &gt;(tee -a out.log)` 作为单个命令运行。 (2认同)