Haui's bytes

news, diary, journal, whatever

Advanced I/O redirection

Recently I had to commit a bunch of changes via SVN. Cause it’s really recommended to review all changes made in the working directory before actually committing the data, I issued a svn status | grep ^M to see all files that have been modified since the last commit. The result was a fairly long list of files and I wanted to check which changes where actually made to each individual file. Of course, every SVN user knows about svn diff or even better svn diff | less , which gives a complete diff of all modified files. However, I don’t really like this output...it just glues diff after diff together an if you scroll too fast, you will miss one or more small but important changes. That’s why i wanted to have a mechanism, that shows one diffed file at a time until I explicitly proceed to the next file. My first approach was a simple one-liner:

svn status | grep ^M | awk '{print $2}' | while read l; do echo "****** $l ******"; svn diff "$l" ; read tmp; done

As you will notice, this doesn’t really work - the two read commands take turns in reading the output of svn status. One elegant solution for this includes the use of the shell builtin exec:

#!/bin/bash
exec 3<&0
svn status | grep ^M | awk '{print $2}' | while read l; do echo "****** $l ******"; svn diff "$l" | less ; read tmp <&3 ;done

The line following the shebang creates a copy of the current stdin (filehandle 0) and assigns it to a new filehandle 3 i.e. 0 and 3 both will read commands from the keyboard being the default in a newly created shell. In the next line filehandle 0 is redirected several times (remember: a | b redirects the stdout of a into the stdin of b), so the first read reads its lines from the awk command. The second read, however, reads its input from filehandle 3 which still has the value that filehandle 0 had in the beginning of the script, i.e. it reads the keyboard input (I also piped svn diff through less, but that’s just a small enhancement which is unrelated to the main problem). This is just a simple example for the powers of bash’s redirection, more complex ones do exist ;)