Keeping it Small and Simple

2007.05.10

Recursive globbing in zsh

Filed under: Zsh — Lorenzo E. Danielsson @ 17:00

A few days ago, a friend of mine wanted to know how many Java source files he had in his home directory. He asked my how to do this, as he was having trouble figuring out how to use the find utility. I simply told him that I don’t normally use find so I’d have to look it up myself. But why use find, when ls can do the job?


% ls ~/**/*.java

Once you have listed all the Java source files, you simply pipe the result into wc and you will have their count:


% ls ~/**/*.java | wc -l
648

The double asterisk (**) will cause zsh to recurse into subdirectories. It is a very powerful feature (and much simpler to use than find, I’m sure you will agree). The double asterisk is a glob, so it can be used other commands as well, not just ls.

Since I used Java files for my first example, let’s stay with them a little longer. When Java source files are compiled, the “binary” result is called a class file (extension .class). If you start running low on hard disk space you may want to remove these, since they can be recreated from the source file. Removing all Java class files from your home directory can be done with:


% rm ~/**/*.class

Of course, you would want to confirm that you are not deleting any important files before doing this. You could check this by issuing a ls ~/**/*.class first.

Let’s look at another small example. I have a pictures directory structure where I have organized my pictures by creating directories for different categories of pictures. I have directories called “family”, “animals”, “astronomy” etc. Several of these directories also contain sub-categories, so for instance, my animals directory has sub-directories called “tigers”, “jaguars”, “pythons”, and so on.

Normally I just dump new pictures into its category directory. Once in a while I decide to do some housekeeping. One of the things I don’t like is file names consisting of capitals. So I want to convert all capitals to lower-case letters. This can be done as follows:

for pic in ~/pictures/**/*[A-Z]*; do 
    mv $pic ${pic:l}
done

For the sake of readability I spread this out in a way that would make your average programmer happy. But, nothing stops you from writing this as:


% for p in ~/pictures/**/*[A-Z]*; do mv $p ${p:l}; done

in your terminal window. So what does this do? It simply finds all files that contain at least one capital letter and converts all upper-case characters to lower-case ones. The mv $p ${p:l} part is where the conversion happens. Remember that mv takes a source file and a destination file. The source file will be given by each iteration of the loop, and the destination will be almost the same, but with a single difference. The :l that you see is called a modifier, and it will convert all the characters in $p to lower-case.

There are of course other ways of converting files to all lower-case characters, including using the powerful zmv. But I’ll leave that as a topic for another post.

I hope that this little introduction to recursive globbing will be helpful to people who are new to the UNIX shell. I also hope it highlights the importance of getting to know your tools well. I all too often meet people who are struggling with simple things just because they don’t take the time to learn how to properly use the tools they have available to them.

If you want to know more about zsh there are lots of resources available. Google is of course a good starting point. The manual pages for zsh are good, but a bit lacking in examples. There is a page called zsh-lovers available on the ‘Net which does have (lots of) examples. It is a good compliment to the manual pages.

If you have other good examples that involve recursive globbing, or good resources for learning zsh, feel free to add them to the comments section.


Edit 2007.05.12: Fixed indentation in code snippet.

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

Create a free website or blog at WordPress.com.