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)
当命令包含管道时,每个子命令都在子shell中运行.所以shell首先为管道的每个部分分配一个子shell,然后第一个部分的子shell执行exec
时不带任何参数,它不执行任何操作并退出.
exec
使用重定向,没有命令被视为特殊情况.从文档:
如果未指定command,则任何重定向在当前shell中生效,返回状态为0.
我花了一段时间才弄清楚如何为 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 重定向到日志文件。如果这些行以相反的顺序出现,则结果将不正确。