One of the new features in Bash 4.0 is coproc. The coproc operator allows you to create a co-process that is associated with the command shell using two channels: one to send data to the co-process, the second to receive from the co-process.
For the first time I found the use of this trying to write a log using
exec redirection. The goal was to optionally allow writing the script output to a log file after running the script (for example, due to the
--log option of the command line).
The main problem with logging output after the script has started is related to the fact that its output could already be redirected (to a file or channel). If we redirect already redirected output, we will not be able to execute the command as it was intended by the user.
The previous implementation was done using named pipes:
From the previous
article :
Here, if the standard output stream of the script is not connected to the terminal, we create a pipe (named pipe located in the file system) using mknod , and with the help of trap we delete it after the script ends. Then we run tee , which in the background connects the input stream with the created channel. Keep in mind that in addition to writing to the file everything received from the input stream, tee also outputs everything to the standard output stream. Also, keep in mind that the tee output stream is sent to the same place as the entire output of the script (the calling tee script). Thus, tee output will go to where the output stream of this script is currently directed (that is, to the output stream redirected by the user or the pipeline specified when the script was called on the command line). Thus, we got a standard tee output where we need it: to a redirection or a pipeline of a user-defined channel.
We can do the same with co-processes:
echo hello if test -t 1; then
In case our standard output goes to the terminal, we simply use
exec to redirect our output to the correct log file. If the output is not connected to a terminal device, then we use coproc to start
tee as a co-process and redirect our output to
tee input and
tee output to redirect to where it was originally planned.
Running
tee using coproc essentially works just like tee in the background (for example,
tee log & ), the main difference is that both channels (input and output) are connected to it. By default,
Bash places the file descriptors of these channels in the
COPROC array.
- COPROC [0] is the file descriptor of the channel connected to the standard output of the co-process;
- COPROC [1] is connected to the standard input of the co-process.
Keep in mind that these channels must be created before using redirects in a team.
Pay attention to the part of the script in which the output of the script is not related to the terminal. The next line duplicates our standard output in file descriptor 7.
exec 7>&1
Then we start
tee with redirecting its output to file descriptor 7.
coproc tee log 1>&7
Now
tee will write everything that he reads from standard input to a file called
log and file descriptor 7, which is our current standard output.
Now we will close the file descriptor 7 (remember that
tee is also “file”, which is open at 7 as standard output):
exec 7>&-
Since we have closed 7, we can use it again, so we transfer the channel connected to input 7
tee :
exec 7>&${COPROC[1]}-
Then we move our standard output to the channel attached to the standard input
tee (our file descriptor 7) by:
exec 1>&7-
And finally, we close the channel attached to the
tee output, since we no longer need it:
eval "exec ${COPROC[0]}>&-"
In this case,
eval is necessary here, because otherwise
Bash considers that the value of
$ {COPROC [0]} is a command. On the other hand, this is not required above (
exec 7> & $ {COPROC [1]} - ), because
Bash knows that “7” initiates work with the file descriptor, and is not considered a command.
Also note the commented line:
This is useful for viewing files opened by the current process.
')
Now we have the desired effect: our standard output will be sent to
tee .
Tee has an “input” to our log file and the recording goes both to the channel and to the file, which was originally planned.
So far I can not think of any other tasks for co-processes, at least not being contrived. See the
bash man page for more information on co-processes.