You’ve avoided learning the ins-and-outs of the Unix find command because it doesn’t play nice within your Subversion and CVS working directories? Well then, I’ve got just the solution!

Don’t want to read my ridiculous blathering? No problem! Just download the free, open source code that “fixes” find.

This article assumes that you have a basic understanding of Unix and the command-line shell. The software only works with GNU bash.

The Unix find and xargs commands are quite useful for quickly processing files in an entire directory tree. One of the most common idioms is to combine them with grep in order to do a “recursive grep” that searches through all of the files in a directory. For example:

find /path/to/haystack/ -type f | xargs grep needle

However, this will fail if any of the path names have spaces or other special characters. To fix that problem, use null separators:

find /path/to/haystack/ -type f -print0 | xargs -0 grep needle

Usually, the “haystack” is simply the current directory. And often, we want to search files with a particular extension, such as .sh files. For example:

find . -name \*.sh -print0 | xargs -0 grep needle

Unfortunately, if you run these commands within a working directory — that is, a directory tree that is managed by a source control system such as CVS or Subversion — you may get more than you bargained for. That’s because these systems create special, “hidden” directories that you aren’t supposed to mess with. With Subversion, it is the ‘.svn’ directory:

ls .svn/
README.txt empty-file format props tmp
dir-props entries prop-base text-base wcprops

CVS uses a similar directory named, well, CVS.

When you consider that every subdirectory in your working directory tree has its own ‘.svn’ (or CVS) directory, it’s easy to see how this can interfere with your recursive greps and whatnot. What to do?

One idea is to use the power of find to skip over those special directories. Unfortunately, find can be tricky to get right. It turns out that the proper syntax is:

find . ! ( -name .svn -prune )

That’s not too pretty, especially when you consider that ! and ( are special characters in both bash and find. So you’d better type:

find . \! \( -name .svn -prune \)

Clearly that’s an error-prone burden, particularly once you connect the dots:

find . \! \( -name .svn -prune \) -name \*.sh -print0 | xargs grep -0 needle

I’ve packaged this into a transparent wrapper that tweaks the command line before find sees it, inserting a filter like the one above. And although find has a non-standard syntax that, among other things, places constraints on the ordering of the options, the resulting logic is non-trivial but manageable.

To use this solution, download the script and place it in your home directory (or anywhere else). Then modify your .bashrc or .bash_profile script to include it:

source ~/

The next time you login, the find wrapper function will be in place. To test it, try the new -debug option:

find -debug . -type f
find . ! ( ( -name .svn -o -name CVS ) -prune ) -type f

Note that, if need be, you can bypass the wrapper using the command builtin. In that case, find is invoked directly and the .svn and CVS directories are not treated specially:

command find . -name .svn