提问人:Harald 提问时间:8/15/2014 更新时间:11/18/2017 访问量:12409
bash exec 将输出发送到管道,如何?
bash exec sending output to a pipe, how?
问:
我尝试执行 bash 本身只是为了重定向输出。如果我使用重定向,比如
exec >bla.log
ls
exec 1>&2
它按预期工作:输出最终进入并在第二件事恢复正常之后,主要是因为句柄 2 仍然绑定到终端。ls
bla.log
exec
现在我想通过管道而不是文件发送输出,一个简单的例子是 .但是,该命令会立即返回。为了弄清楚发生了什么,我这样做了:exec | cat >bla.log
exec | bash -c 'echo $$; ls -l /proc/$$/fd /proc/23084/fd'
其中 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
正如我们所看到的,子进程 24002 确实在监听管道。但它肯定不是父进程 23084,它打开了这个管道。
知道这是怎么回事吗?
答:
当命令包含流水线时,每个子命令都在子 shell 中运行。因此,shell 首先为管道的每个部分分叉一个子 shell,然后第一个部分的子 shell 在没有参数的情况下执行,这不执行任何操作并退出。exec
exec
具有重定向功能,并且没有命令被视为特殊情况。从文档中:
如果未指定命令,则任何重定向都会在 当前 shell,返回状态为 0。
什么
实现本来可能编写的内容的正确方法
exec | cat >bla.log
是
#!/bin/bash
# ^^^^ - IMPORTANT: not /bin/sh
exec > >(cat >bla.log)
为什么
这是因为是进程替换;它被替换为文件名(如果可能,则为格式,否则为临时 FIFO),当写入时,该文件名将传递到封闭进程的 stdin。(是类似的,但在另一个方向上:被替换为类似文件对象的名称,该对象在读取时将返回给定进程的 stdout)。>()
/dev/fd/NN
<()
因此,大致等同于以下内容(在不提供 、 或类似功能的操作系统上):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.
评论
ts
我花了一段时间才弄清楚如何让 stdout 和 stderr 的重定向组合正确,所以这可能对其他人有用。
以下示例演示了在区分 stdout 和 stderr 时将 tee 用作“管道”目标的重定向。
#!/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
在 CLI 中运行此命令,并将 stdout 重定向到 /dev/null,您只能看到 stderr 消息。此外,在每个日志文件中,您只能看到相关消息。
请注意,线条和线条的顺序很重要。从本质上讲,我们首先要将 stderr 重定向到错误文件,然后将 stdout 重定向到日志文件。如果这些行以相反的顺序出现,则结果将不正确。exec 2>
exec >
评论
exec
exec 2> >(tee -a err.log >&2) > >(tee -a out.log)
评论
exec 1>&2
并不完全是“恢复正常”——将 stdout 定向到 stderr。恢复正常会是这样的exec 3>&1; exec 1>blah.log; ls; exec 1>&3; exec 3>&-