There are odd times when you may wish to use the stdout of a program as the stdin to more than one follow-on. bash lets you do this using a > to a command instead of just a file. I think the idea is that you just have to make sure the command is in ( ) and it works. One up to bash, but what about somthing that will work on standard shells?
The answer is to use a named pipe or fifo, and it’s a bit more hassle – but not too much more.
As an example, lets stick to the “hello world” theme. The command sed s/greeting/hello/
will replace the word “greeting” on stdin with “world” on stdout – everything else will pass through unchanged. Are you okay with that? Try it if you’re not comfortable with sed
Now I’m going to send a stdout to two different sed
instances at once:
sed s/greeting/hello/ sed s/greeting/world/
To get my stdout for test purposes I’ll just use “echo greeting”. To pipe it to a single process we would use:
echo greeting | sed s/greeting/hi/
Our friend for the next part is the tee
command (as in a T in plumbing). It copies stdin to two different places, stdout and (unfortunately for us) a file. Actually it can copy it to as many files as you specify, so it should probably have been called “manifold”, but this is too much to ask for an OS design that spells create without the training ‘e’.
Tee won’t send output to another processes stdin directly, but the files can be a fifos (named pipes). In older versions of UNIX you created a pipe with the mknod
command, but since FreeBSD moved to character only files this is deprecated and you should use mkfifo
instead. Solaris also uses mkfifo
, and it came in as far back as 4.4BSD, but if you’re using something old or weird check the documentation. It’ll probably be something like mknod <pipename>
.
Here’s an example of it in action, solving our problem:
mkfifo mypipe sed s/greeting/hello/ < mypipe & echo greeting | tee mypipe | sed s/greeting/world/ rm mypipe
It works like this: First off we create a named pipe called mypipe. Next (and this is the trick), we run the first version of sed, specifying its input to come from “mypipe”. The trailing ‘&’ is very important. In case it had passed you by until now, it means run this command asynchronously – or in background mode. If we omitted it, sed
would sit there waiting for input it would never receive, and we wouldn’t get the command prompt back to enter the further commands.
The third line has the tee command added to send a copy of stdout to the pipe (where the first sed
is still waiting). The first copy is piped in the normal way to the second sed
.
Finally we remove the pipe. It’s good to be tidy but it doesn’t matter if you want to leave it there use it again.
As a refinement, pipes with names like “mypipe” in the working directory could lead to trouble if you forgot to delete it or if another job picks the same name. Therefore it’s better to create them in the /tmp directory and add the current process ID to the name in order to avoid a clash. e.g.:
mkfifo /tmp/mypipe.1.$$
$$
expands to the process-ID, and I added a .1. in the example so I can expand the scheme to have multiple processes receiving the output – not just one. You can use tee to send to as many pipes and files as you wish.
If you run the example, you’ll probably get “hello world” output on two lines, but you might get “world hello”. The jobs have equal status, so there’s no way to of knowing which one will complete first, unless you decided to put sleep
at the start of one for force the issue.
When I get around to it a more elaborate example might appear here.