bash exec 将输出发送到管道,如何?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25316705/
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
bash exec sending output to a pipe, how?
提问by Harald
I experiment with exec'ing the bash itself only to redirect the output. If I use redirection like
我尝试执行 bash 本身只是为了重定向输出。如果我使用重定向
exec >bla.log
ls
exec 1>&2
it works as expected: the ls
output ends up in bla.log
and after the second exec
things are back to normal, mainly because handle 2 is still bound to the terminal.
它按预期工作:ls
输出bla.log
在第二exec
件事恢复正常之后结束,主要是因为句柄 2 仍然绑定到终端。
Now I thought to send the output through a pipe instead of into a file, a trivial example being exec | cat >bla.log
. However, the command immediately returns. To figure out what is going on, I did this:
现在我想通过管道而不是将输出发送到文件中,一个简单的例子是exec | cat >bla.log
. 但是,该命令会立即返回。为了弄清楚发生了什么,我这样做了:
exec | bash -c 'echo $$; ls -l /proc/$$/fd /proc/23084/fd'
where 23084 is the bash currently running and got this:
其中 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
As we can see, the sub-process 24002 is indeed listening to a pipe. But it certainly is not the parent process, 23084, which has this pipe open.
可以看到,子进程24002确实在监听管道。但它肯定不是父进程 23084,它打开了这个管道。
Any ideas what is going on here?
任何想法这里发生了什么?
采纳答案by Barmar
When a command contains a pipeline, each subcommand is run in a subshell. So the shell first forks a subshell for each part of the pipeline, and then the subshell for the first part executes exec
with no arguments, which does nothing and exits.
当命令包含管道时,每个子命令都在子 shell 中运行。所以shell首先为管道的每个部分派生一个子shell,然后第一部分的子shellexec
不带参数执行,它什么也不做并退出。
exec
with redirection and no command is treated as a special case. From the documentation:
exec
使用重定向并且没有命令被视为特殊情况。从文档:
If command is not specified, any redirections take effect in the current shell, and the return status is 0.
如果不指定command,则任何重定向都在当前shell中生效,返回状态为0。
回答by Charles Duffy
What
什么
The proper way to implement something that might otherwise be written
实现可能以其他方式编写的内容的正确方法
exec | cat >bla.log
is
是
#!/bin/bash
# ^^^^ - IMPORTANT: not /bin/sh
exec > >(cat >bla.log)
Why
为什么
This is because >()
is a process substitution; it's replaced with a filename (of the /dev/fd/NN
form if possible, or a temporary FIFO otherwise) which, when written to, will deliver to the stdin of the enclosed process. (<()
is similar, but in the other direction: being substituted with the name of a file-like object which will, when read, return the given process's stdout).
这是因为>()
是一个进程替换;它被替换为一个文件名(/dev/fd/NN
如果可能,采用这种形式,否则为临时 FIFO),该文件名在写入时将传送到封闭进程的标准输入。(<()
是类似的,但在另一个方向:被替换为类文件对象的名称,该对象将在读取时返回给定进程的标准输出)。
Thus, exec > >(cat >bla.log)
is roughly equivalent to the following (on an operating system that doesn't provide /dev/fd
, /proc/self/fds
, or similar):
因此,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.
回答by JinnKo
It took me a while to figure out how to get the combination of redirections right for stdout and stderr, so this might be useful to others.
我花了一段时间才弄清楚如何为 stdout 和 stderr 获得正确的重定向组合,所以这可能对其他人有用。
The following examples illustrate using redirections with tee as a "pipe" target while distinguishing stdout and 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 this in the CLI with stdout redirected to /dev/null and you only see the stderr messages. Plus within each log file you only see the relevant messages.
在 CLI 中运行此命令,并将 stdout 重定向到 /dev/null,您只会看到 stderr 消息。此外,在每个日志文件中,您只能看到相关消息。
Note that the order of the exec 2>
line and the exec >
lines is important. Essentially we first want to redirect stderr to an error file, then stdout to a log file. If these lines appear in the opposite order the results would not be correct.
请注意,exec 2>
行和exec >
行的顺序很重要。本质上,我们首先要将 stderr 重定向到错误文件,然后将 stdout 重定向到日志文件。如果这些线以相反的顺序出现,则结果将不正确。