Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm trying to mass rename the files from one extension to another (background: use haml instead of erb in my rails app). I get following output when issuing rename command:

% zmv '**/*.erb' $1.haml    
zmv: error(s) in substitution:
app/views/l/links/index.html.erb and app/views/index/index.html.erb both map to .haml
app/views/l/links/new.html.erb and app/views/l/links/index.html.erb both map to .haml
app/views/l/links/show.html.erb and app/views/l/links/new.html.erb both map to .haml
app/views/l/links/stats.html.erb and app/views/l/links/show.html.erb both map to .haml
app/views/layouts/application.html.erb and app/views/l/links/stats.html.erb both map to .haml
app/views/u/profiles/_form.erb and app/views/layouts/application.html.erb both map to .haml
app/views/u/profiles/edit.html.erb and app/views/u/profiles/_form.erb both map to .haml
app/views/u/profiles/show.html.erb and app/views/u/profiles/edit.html.erb both map to .haml
app/views/u/user_sessions/new.html.erb and app/views/u/profiles/show.html.erb both map to .haml
app/views/u/users/_form.erb and app/views/u/user_sessions/new.html.erb both map to .haml
app/views/u/users/new.html.erb and app/views/u/users/_form.erb both map to .haml
app/views/u/users/show.html.erb and app/views/u/users/new.html.erb both map to .haml

Can anyone point me to the right direction of fixing this?

share|improve this question
do you want an answer to, how do I do this with zmv? or an answer to, how can mass rename files from one extension to another? – xenoterracide Aug 22 '10 at 8:12
@xenoterracide - yes ,) – Eimantas Aug 23 '10 at 16:11
up vote 5 down vote accepted

I think what you really want is something like this:

% zmv '(**/)(*).erb' '$1/$2.haml'
#      ^$1  ^$2

You need to use the parenthesis to create match groups, and you create a match group for the path to the file, and then a match group for the file name. Also, you need to make sure the second argument is also in single-quotes for zmv.

Also, it's a really good idea to test zmv commands with '-n' before you run them (-n will tell you what will be renamed, but not actually rename anything.)

share|improve this answer
Unfortunately (at least up to zsh 4.3.10), (**)/ matches exactly one directory level, i.e., ** loses its special meaning and is equivalent to *. So this command works but doesn't have the intended meaning. – Gilles Aug 22 '10 at 10:16
@Gilles: Yeah, I had it right the first time, and then I edited it because I'm stupid. However, (**/) works properly, so I'm going to update the post to what I had originally so that it's correct. – clee Aug 22 '10 at 11:16

You need to tell zsh what $1 refers to. There are two possibilities:

  • Use parentheses around parts of the source pattern that you want to use. For example, in zmv '(*)/(*).erb' '$1/$2.haml', '$1' means what is matched by the first * and $2 means what is matched by the second $2.

    [EDIT (thanks to clee for pointing out that (**/) works)] Parentheses are a little awkward to use with multiple directory levels. If you write (**), the double asterisk loses its special meaning (only a single directory level is matched). And you mostly can't use / inside parentheses, so (**/*) is not a valid pattern. However, the special case (**/) works, so you can write zmv '(**/)(*).erb' '$1$2.haml'.

  • Use the -w option, in which case each $N corresponds to the text matched by the *N*th wildcard in the source pattern. For example, zmv -w '**/*.erb' '$1/$2.haml' does what you want.

Note that you must always use single quotes around the replacement text (or use \$), otherwise the $s are expanded by the shell before they get to the zmv built-in command.

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.