I want to go through a bunch of directories and rename all files that end in _test.rb to end in _spec.rb instead. It's something I've never quite figured out how to do with bash so this time I thought I'd put some effort in to get it nailed. I've so far come up short though, my best effort is:

find spec -name "*_test.rb" -exec echo mv {} `echo {} | sed s/test/spec/` \;

NB: there's an extra echo after exec so that the command is printed instead of run while I'm testing it.

When I run it the output for each matched filename is:

mv original original

i.e. the substitution by sed has been lost. What's the trick?

  • 1
    I can tell you what's wrong, but I'm not sure how to tell you to fix it: bash is immediately executing echo {} | sed s/test/spec/, which produces {}, then bash executes the find command. Have you checked to see whether your distribution has a good rename program?
    – DerfK
    Jan 25, 2011 at 13:37
  • 1
    Please don't cross-post. Jan 25, 2011 at 20:36

3 Answers 3


If you don't have rename (it's a really short Perl script) or your rename is the more simplistic util-linux version or you simply want to know how to make your command work: you just need to send it to the shell for execution:

find spec -name "*_test.rb" -exec sh -c 'echo mv "$1" "$(echo "$1" | sed s/test.rb\$/spec.rb/)"' _ {} \;

Too complicated. If you have the rename command available, you could try the following:

find . -name "*_test.rb" -print0 | xargs -0 rename "s/_test/_spec/" 

This finds the relevant files, sends them to xargs which in turn uses the rename tool (which renames files according to the given perl regex).

  • +1 for -print0. Also, I'd make the regexp pattern as axplicit as possible: "s/_test.rb$/_spec.rb/" just to be on the safe side. Jan 25, 2011 at 15:34
  • note that only works if you happen to have the perl script version of rename, not the standard rename that comes with util-linux. Jan 25, 2011 at 21:35

From the man page of util-linux's rename:

   rename '_with_long_name' '' file_with_long_name.*

will remove the substring in the filenames.

So this command works if you do not have the Perl version of rename:

find . -name "*_test.rb" -print0 | xargs -0 rename "_test..rb" "_spec/rb" 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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