Bash exec sends the output to the pipe, how?

I am experimenting with exec'ing bash itself only to redirect output. If I use redirection like

exec >bla.log ls exec 1>&2 

It works as expected: the ls output ends in bla.log , and after the second exec everything returns to its normal state, mainly because descriptor 2 is still bound to the terminal.

Now I decided to send the result through the channel, and not to the file, a trivial example - exec | cat >bla.log exec | cat >bla.log . However, the team returns immediately. To find out what happens, I did the following:

 exec | bash -c 'echo $$; ls -l /proc/$$/fd /proc/23084/fd' 

where 23084 - this bash is currently working and got this:

 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, subprocess 24002 really listens for the pipe. But this, of course, is not the parent process, 23084, which opens this channel.

Any ideas on what's going on here?

+7
bash io-redirection
source share
3 answers

When a command contains a pipeline, each subcommand is executed in a subshell. So, the shell first expands the subshell for each part of the pipeline, and then the subshell for the first part exec without arguments, which does nothing and exits.

exec redirected, and no command is considered a special case. From the documentation:

If no command is specified, any redirects take effect in the current shell, and the return status is 0.

+4
source share

what

The right way to implement what would otherwise be written

 exec | cat >bla.log 

is an

 #!/bin/bash # ^^^^ - IMPORTANT: not /bin/sh exec > >(cat >bla.log) 

Why

This is because >() is the substitution process ; it is replaced by the file name (in the case of /dev/fd/NN , if possible, or a temporary FIFO otherwise), which, when written to it, will be transferred to the stdin of the attached process. ( <() similar, but in a different direction: it is replaced by the name of the file object, which, when reading, returns the given stdout process).

Thus exec > >(cat >bla.log) roughly equivalent to the following (on an operating system that does not provide /dev/fd , /proc/self/fds or similar):

 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. 
+11
source share

It took me a while to figure out how to get a combination of redirects for stdout and stderr, so this might be useful for others.

The following examples illustrate the use of redirection with a tee as a pipe target in distinguishing between stdout and 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 when stdout is redirected to / dev / null and you only see stderr messages. Plus, in each log file you see only the corresponding messages.

Note that the order of the lines exec 2> and exec > important. Essentially, we first want to redirect stderr to the error file, and then stdout to the log file. If these lines appear in the reverse order, the results will be incorrect.

0
source share

All Articles