ZSH Gem #1: Programmable file renaming
Welcome to the first article of this year's Advent calendar series. The last Advent calendar was extremely successful so I thought it would be a good idea to continue this.
The topic for the Advent calendar series 2011 is “24 Outstanding ZSH Gems” because the Z Shell is my absolute favorite shell. There's nothing ZSH can't do except the dishes. In my opinion it's much more powerful than any other shell out there. If you are not a ZSH user (yet?), you're still very welcome to enjoy this series as well. Maybe then you'll make the switch. And if not: some features also work similarly in Bash, although they might be not so comfortable there.
Now let's begin with programmable renaming of files or batch renaming. That sounds very abstract, but let me show you what I mean.
The normal procedure to move or rename files is to use mv
:
mv source dest
This is done pretty quickly and you can use the normal globbing functions your shell provides (which BTW are very mighty in ZSH, but I will explain that in a later article). But what if you could use the expanded globbing parts of source
in dest
? That would make batch renaming of files a lot easier. Normally you would do this by looping over all files, checking if their names match a pattern and then renaming them. Alternatively, you could use find
and do the renaming with the -exec
parameter, but that is also a bit tricky. With zmv
, however, you can do that with just one simple command.
Let's assume you want to rename all files ending with .txt
to .html
. We can do that:
autoload -U zmv
zmv '(*).txt' '$1.html'
The first line just loads the zmv module. You only need to do that once per session. Normally you would put that into your .zshrc
file under your home directory. The second line is the main line. Here we perform the rename operation. You can see: we have put the globbing operator *
into parentheses. That creates a back reference which we can then use in the second parameter as the variable $1
. More back references would of course be available through $2
, $3
etc. You might know this from other programming languages such as Perl, PHP or JavaScript when working with regular expressions.
This was one very simple command. But we can do more. I won't explain the more advanced globbing operators here (I'll do that in a later article), but one thing I can show you here is parameter substitution (or parameter expansion, as it's called in ZSH). That's something Bash knows as well, but in combination with zmv
this becomes very handy. Parameter expansion is a feature to perform operations on shell parameters, i.e. on variables. So we can, e.g., do search and replace operations. If we want to replace all underlines in file names with dashes, we could either use:
zmv '(*)_(*)' '$1-$2'
or we could utilize parameter expansion:
zmv '(*_*)' '${1//_/-}'
The first one looks a bit ugly but less complicated, but the second one is much more flexible since we only need one back reference and can do any string operation on it. For instance, we can make all file names uppercase:
zmv '(*)' '${(U)1}'
or lowercase:
zmv '(*)' '${(L)1}'
or replace the file names with their lengths (usually not a good idea):
zmv -n '(*)' 'This file name was ${#1} characters long'
or whatever. Parameter expansion is a very complex topic, but I'll put a link for that below this article.
Be aware, that only globbing operators within parentheses are saved as back references. If you want to have back references of all globbing parts (i.e. implicit parentheses), use the zmv
parameter -w
.
One last thing to mention: you should always do a dry run first. You can do that with the parameter -n
. That will show which operations would be performed without actually executing any rename or move operation.
Read more about zmv:
- zsh.sf.net: zmv documentation
- zshwiki.org: zmv introduction
- ZSH-LOVERS: zmv examples
- zsh.sf.net: Parameter expansion