Bash Co-Processes
One of the new features in bash 4.0 is the coproc statement. The coproc statement allows you to create a co-process that is connected to the invoking shell via two pipes: one to send input to the co-process and one to get output from the co-process.
The first use that I found for this I discovered while trying to do logging and using exec redirections. The goal was to allow you to optionally start writing all of a script's output to a log file once the script had already begun (e.g. due to a --log command line option).
The main problem with logging output after the script has already started is that the script may have been invoked with the output already redirected (to a file or to a pipe). If we change where the output goes when the output has already been redirected then we will not be executing the command as intended by the user.
The previous attempt ended up using named pipes:
#!/bin/bash
echo hello
if test -t 1; then
# Stdout is a terminal.
exec >log
else
# Stdout is not a terminal.
npipe=/tmp/$$.tmp
trap "rm -f $npipe" EXIT
mknod $npipe p
tee <$npipe log &
exec 1>&-
exec 1>$npipe
fi
echo goodbye
From the previous article:
Here, if the script's stdout is not connected to the terminal, we create a named pipe (a pipe that exists in the file-system) using mknod and setup a trap to delete it on exit. Then we start tee in the background reading from the named pipe and writing to the log file. Remember that tee is also writing anything that it reads on its stdin to its stdout. Also remember that tee's stdout is also the same as the script's stdout (our main script, the one that invokes tee) so the output from tee's stdout is going to go wherever our stdout is currently going (i.e. to the user's redirection or pipeline that was specified on the command line). So at this point we have tee's output going where it needs to go: into the redirection/pipeline specified by the user.
We can do the same thing using a co-process:
echo hello
if test -t 1; then
# Stdout is a terminal.
exec >log
else
# Stdout is not a terminal.
exec 7>&1
coproc tee log 1>&7
#echo Stdout of coproc: ${COPROC[0]} >&2
#echo Stdin of coproc: ${COPROC[1]} >&2
#ls -la /proc/$$/fd
exec 7>&-
exec 7>&${COPROC[1]}-
exec 1>&7-
eval "exec ${COPROC[0]}>&-"
#ls -la /proc/$$/fd
fi
echo goodbye
echo error >&2
In the case that our standard output is going to the terminal then we just use exec to redirect our output to the desired log file, as before. If our output is not going to the terminal then we use coproc to run tee as a co-process and redirect our output to tee's input and redirect tee's output to where our output was originally going.
Running tee using the coproc statement is essentially the same as running tee in the background (e.g. tee log &), the main difference is that bash runs tee with both its input and output connected to pipes. Bash puts the file descriptors for those pipes into an array named COPROC (by default):
- COPROC[0] is the file descriptor for a pipe that is connected to the standard output of the co-process
- COPROC[1] is connected to the standard input of the co-process.
Note that these pipes are created before any redirections are done in the command.
Focusing on the part where the original script's output is not connected to the terminal. The following line duplicates our standard output on file descriptor 7.
exec 7>&1
Then we start tee with its output redirected to file descriptor 7.
coproc tee log 1>&7
So tee will now write whatever it reads on its standard input to the file named log and to file descriptor 7, which is our original standard out.
Now we close file descriptor 7 with (remember that tee still has the "file" that's open on 7 opened as its standard output) with:
exec 7>&-
Since we've closed 7 we can reuse it, so we move the pipe that's connected to tee's input to 7 with:
exec 7>&${COPROC[1]}-
Then we move our standard output to the pipe that's connected to tee's standard input (our file descriptor 7) via:
exec 1>&7-
And finally, we close the pipe connected to tee's output, since we don't have any need for it, with:
eval "exec ${COPROC[0]}>&-"
The eval here is required here because otherwise bash thinks the value of ${COPROC[0]} is a command name. On the other hand, it's not required in the statement above (exec 7>&${COPROC[1]}-), because in that one bash can recognize that "7" is the start of a file descriptor action and not a command.
Also note the commented command:
#ls -la /proc/$$/fd
This is useful for seeing the files that are open by the current process.
We now have achieved the desired effect: our standard output is going into tee. Tee is "logging" it to our log file and writing it to the pipe or file that our output was originally going to.
As of yet I haven't come up with any other uses for co-processes, at least ones that aren't contrived. See the bash man page for more about co-processes.
Mitch Frazier is an Associate Editor for Linux Journal.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Designing Electronics with Linux | May 22, 2013 |
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
- Linux Systems Administrator
- New Products
- Senior Perl Developer
- Technical Support Rep
- UX Designer
- Web & UI Developer (JavaScript & j Query)
- Designing Electronics with Linux
- Dynamic DNS—an Object Lesson in Problem Solving
- Using Salt Stack and Vagrant for Drupal Development
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Have you tried Boxen? It's a
4 hours 7 min ago - seo services in india
8 hours 39 min ago - For KDE install kio-mtp
8 hours 40 min ago - Evernote is much more...
10 hours 40 min ago - Reply to comment | Linux Journal
19 hours 25 min ago - Dynamic DNS
19 hours 59 min ago - Reply to comment | Linux Journal
20 hours 58 min ago - Reply to comment | Linux Journal
21 hours 48 min ago - Not free anymore
1 day 1 hour ago - Great
1 day 5 hours ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Featured Jobs
| Linux Systems Administrator | Houston and Austin, Texas | Host Gator |
| Senior Perl Developer | Austin, Texas | Host Gator |
| Technical Support Rep | Houston and Austin, Texas | Host Gator |
| UX Designer | Austin, Texas | Host Gator |
| Web & UI Developer (JavaScript & j Query) | Austin, Texas | Host Gator |
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?



Comments
Syntax Error
It is a valid syntax and it works .Its called process substitution.Here you redirect the standard output to the file descriptor created by bash from which tee reads from, The standard output of tee is not being redirected so in the end tee writes to the standard output and the log file.
Syntax Error
I'm familiar with process substitution, what I missed in your example was the space between the two greater than signs when I tested it.
echo hello set +o posix if test -t 1; then # Stdout is a terminal. exec >log else # Stdout is not a terminal. exec > >(tee log) # ^ WHITESPACE REQUIRED HERE #ls -la /proc/$$/fd fi echo goodbye echo error >&2One important note is that process substitution is not enabled if bash is running in posix mode (in which case you will get a syntax error). Add a set +o posix at the start of your script to turn off posix mode.
That's a great solution. Thanks Anshul.
Mitch Frazier is an Associate Editor for Linux Journal.
Excellent example of using
Excellent example of using coproc!
The following can be shortened from:
exec 7>&-
exec 7>&${COPROC[1]}-
exec 1>&7-
to:
exec 1>&${COPROC[1]}
Shorter Code
Your shorter code does work but it leaves some files (pipes actually) open that don't need to be open. Running my solution with an "ls -la /proc/$$/fd" after the above code shows:
Using your code it shows:
The following code (thanks to your push) closes things and is slightly shorter:
exec 7>&- exec 1>&${COPROC[1]}-Mitch Frazier is an Associate Editor for Linux Journal.
Thanks, Mitch, I realized my
Thanks, Mitch, I realized my mistake, but it was too late, the button was already pushed.
Thanks for the excellent high tech tips! GB
LJ Account
If you create a linuxjournal.com account and login to leave comments you should be able to edit them afterwards and avoid the "oh crap" moments.
Mitch Frazier is an Associate Editor for Linux Journal.
I think you can do this
I think you can do this instead:
exec > >(tee log)
Syntax Error
No that doesn't work. It's not even valid syntax.
Mitch Frazier is an Associate Editor for Linux Journal.
What does this step do?
In the following step what is that -(minus) for? Is it a typo or does it have any special meaning?
The -(minus) closes file
The -(minus) closes file descriptors after duplicating. From man:
Moving File Descriptors
The redirection operator
[n]<&digit-
moves the file descriptor digit to file descriptor n, or the standard
input (file descriptor 0) if n is not specified. digit is closed after
being duplicated to n.
Similarly, the redirection operator
[n]>&digit-
moves the file descriptor digit to file descriptor n, or the standard
output (file descriptor 1) if n is not specified.
To be honest, to me the
To be honest, to me the traditional first example looks very much simpler and more understandable.
I find it quite difficult to really understand what the advantage of coproc really is, but after all, i am not doing this kind of stuff a lot.