Friday, July 5, 2019

How to get the find command to exclude a directory

A friend of mine wanted to use the find command to search certain files in his home directory, but excluding one particular sub-directory.  I thought I could do that in a minute.  But that turned out to be more than a one minute challenge.  Redirecting stderr to /dev/null and thus avoiding to look at the errors was not what he wanted.  He wanted the find command to not step into the particular directory.  Because that directory contained way too many files and so find command was taking too long to finish.

Google told us, -prune could get us to exclude a directory.

When not knowing exactly how to do something with the find command, consulting the manual page is a good place to start.

       -prune True;  if  the  file  is  a  directory,  do not descend into it. If -depth is given, false; no effect.  Because -delete implies -depth, you cannot usefully use -prune and -delete together.

Well, this did not provide clarity to me.  So I explored further.

-prune stops the find command from entering the mentioned directory.  But only -prune is not sufficient to get us what we want.  -prune is an action (like -exec), and not a test (like -type).  -prune alters the list on which further operation is done.  -prune returns true when the file or directory is skipped, and returns false when the file or directory is not skipped.

$ find .  -path ./perl_modules -prune

In the above command, -prune stops the find command from entering the ./perl_modules directory.  After this, we need to use the remaining list and do with it whatever we wanted to.  For this the -o (logical OR) is useful.  The -o (logical OR) provides the list wherever action resulted in false.   This is our intended list (after eliminating what we wanted to exclude).

My friend wanted to list all the files that have aa in their names. 

$ find . -path ./perl_modules -prune -o  -name '*aa*'  -print

In this output, we found that some errors were also printed, as listed below.

find: `./SoNASInstall': Permission denied
find: `./.jazz5': Permission denied
find: `./.metadata': Permission denied


Earlier, I used redirect stderr to /dev/null and thus avoid looking at the errors.

$ find . -path ./perl_modules -prune -o  -name '*aa*'  -print 2>/dev/null

But even better is to eliminate the unwanted files in the list that is provided to further parts of the find command.  We could do this, using the new -prune trick we have learned.

$ find . -path ./perl_modules -prune -o  -name '.?*' -prune -o  -name '*aa*' -print

I chose to go down the rabbit hole some 20 years ago, when I learned SCO Unix system 5.  And I am still discovering how deep it is.  Oh wait, did I choose to go down the rabbit hole, or did it simply happened to me.  That got me thinking, what is choice?  Is it an illusion?  The Merovingian.  Hell ya.  Where are my stud shoes.  Let me get my gear ready for the football game tomorrow morning.  And escape from the rabbit hole, even for a few hours.

2 comments: