Keeping it Small and Simple

2008.01.18

Zsh Quickie: lowercasing files

Filed under: Zsh — Tags: — Lorenzo E. Danielsson @ 22:37

Sometimes I can’t help but being amazed at the complex operations some people are ready to perform simple tasks.

A very common example: assume that you have a bunch of files in a directory and want to convert them all to lowercase. I’ve seen people write scripts for this. I’ve seen others up open up one of those GUI file manager thingies and rename the files one by one. Yet others will frantically browse through the sed manual, trying to figure out which regex will save the day. In either case, the operation tends to be accompanied by heavy breathing and bucket-loads of sweat. *sigh*

For a zsh user, no matter how you try to complicate it, it *is* a one-liner. The following is easy to understand and to memorize:

% for file in *; do mv $file ${file:l}; done

If you wanted to operate only on JPEG files:

% for img in *.jpg; do mv $img ${img:l}; done

If you are in a state of temporary or permanent insanity and need to upper-case instead:

% for f in *; do mv $f ${f:u}; done

There, no need anymore to create perl scripts, suffer sed or wait 20 minutes for you GUI file manager to start up ever again. Just keep it simple. This probably works in bash as well, but don’t take my word for it. I only use zsh and ksh.

Advertisements

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.

Blog at WordPress.com.