A couple of weeks ago I heard about named pipes, a very neat feature of unix-like systems. This feature allowed me to speed up my TDD cycle. In general, though, I think named pipes fall into the “good-to-know” category for your developer toolbelt.
First off all, a unix pipeline allows a set of processes to have their I/O chained together. A typical example that you’ve probably seen or used before is:
$ ls -l | grep Documents drwx------+ 3 jassa staff 102 Feb 6 19:59 Documents
|Each pipe (||) connects the output of the left program, to the input of the right program. In this example we pass the output of
A traditional pipe lives as long as the processes involved are running. A named pipe, on the other hand, is persistent and sits in your system like any other file. Whatever you write to it, can be read elsewhere. Here’s an example (we need two terminal sessions because reading/writing to a named pipe is I/O blocking).
# Terminal 1 $ mkfifo myNamedPipe # creates a file named “myNamedPipe” $ echo "Foo" > myNamedPipe # send “Foo” to myNamedPipe # ..this process hangs until we read the contents of myNamedPipe elsewhere # Terminal 2 $ cat myNamedPipe Foo # returns immediately, and the process in terminal 1 also returns
Reading and writing to named pipes are I/O blocking operations. Both processes depend on each other and will be blocked until you read the contents of your named pipe. Our previous example with an anonymous pipe could become:
# Terminal 1 $ ls -l | grep $(cat myNamedPipe) # hangs… # Terminal 2 $ echo “Documents” > myNamedPipe # Back in terminal 1 our process returns… drwx------+ 3 jassa staff 102 Feb 6 19:59 Documents
By now you should start to realize the power of named pipes. I switched to Vim recently (most people I work with use it, so in order to pair effectively I left Emacs for a while), and on top of that I started working on a Java project that uses maven to manage its build. I decided to avoid the typical slow and bloated IDEs, and just try a Vim workflow instead. I value simplicity, not just in my code but also in my tools, and named pipes allowed me to achieve a tight feedback loop without the overhead an IDE and third-party libraries bring into the mix.
Basically I created a custom autorunner for whenever my tests change, using Vim and maven.
My “manual” TDD workflow looks like this:
- Make and save changes to SomeClassTest.java
mvn -Dtest=SomeClassTest clean testin a command prompt
In order to automate the 2nd step, I had to setup a couple of things in my terminal.
- Create a named pipe (in my case
Run an endless loop that evaluates the name of the desired test class
$ while true; do mvn -Dtest=$(cat .test-class) clean test; done
This endless loop will be blocked waiting to read from the
.test-classpipe. If we
echo “SomeClassTest” > .test-classit will run the given test and block again waiting to read.
The last step is making Vim write the class name to the pipe every time we save our changes. In my project’s local
.vimrc (See the exrc option):
:autocmd BufWritePost *Test.java :call RunTest() function! RunTest() :call system('echo ' . expand('%:tr') . ' > .test-pipe') endfunction
This function runs every time we save a file that ends with
Test.java, and passes the current file name to our named pipe. Now my tests run automatically whenever I save my changes. It might not look like much, but consider the seconds saved from every time I had to switch to another tab, hit the up arrow and press enter (or alternatively
reverse-i-search for “test”).
What I liked the most about this approach is that with a couple tweaks you can reuse it for pretty much any language and environment.
This is only the first use that I give to named pipes. Like I said, it’s probably one of those features that are useful to know about, but probably not immediately. I don’t think I have any other use for them right now, but I bet if I encounter a problem they can solve, they’ll pop up in my head.