39

Attempting to rename a bunch of files.

I can rename any instances of foo with bar in the current directory with:

ls . | awk '{print("mv "$1" "$1)}' | sed 's/foo/bar/2' | /bin/sh

What can I add to make it recursive?

Edit/My solution

I don't know/understand this shell type stuff so I did it with some (pretty dirty) Ruby:

5.times do
  Dir["**/*"].each do |f|
    file_name = File.absolute_path f
    should_rename = file_name.include? "yolo"
    new_file_name = file_name.gsub("yolo", "#{@project_name}")
    File.rename(f, new_file_name) if (should_rename and File.exists? f)
  end
end
Adam Waite
  • 19,175
  • 22
  • 126
  • 148

5 Answers5

71

This has been asked: Recursive batch rename

With your example, you could go with:

brew install rename
find . -exec rename 's|foo|bar|' {} +
Community
  • 1
  • 1
Marcel Steinbach
  • 1,109
  • 6
  • 9
  • 11
    You can also filter the set of files that will be replaced by name. For example I needed to recursively rename all *.java files to *.txt so I did it like so: `find . -name "*.java" -exec rename 's|\.java|\.txt|' {} +` – Noam Ben Ari May 15 '14 at 12:10
  • 1
    `rename` can be used with a glob too, for example `rename "s/.html/.twig/gi" templates/**/*` – Matt Fletcher Apr 29 '19 at 09:45
  • Works but there is a catch. It replaces the first occurrence. For example, if I want to remove all the "_" from a file example_file_name.jpg, it would rename it to examplefile_name.jpg – BLC Mar 04 '20 at 11:38
  • `find . -exec rename 's|foo|bar|g' {} +` – Marcel Steinbach Mar 05 '20 at 15:17
26

The rename utility also provides a syntax that does not require knowledge of regex. Once you have installed the utility

brew install rename

The option -s will replace the first instance of foo by bar:

find . -exec rename -s 'foo' 'bar' {} +

And the option -S will replace all instances:

find . -exec rename -S '_' ' ' {} +

This is really the same solution that Marcel Steinbach provided above, just a different syntax.

Community
  • 1
  • 1
altabq
  • 1,322
  • 1
  • 20
  • 33
20

Nice, clean, zero-dependency (so no brew install rename or whatever) from this webpage (archived version).

In this sample case, I'm swapping from .js to .ts to move a project from vanilla javascript to typescript, so I want to rename everything from *.js to *.ts first.

find . -iname "*.js" -exec bash -c 'mv "$0" "${0%\.js}.ts"' {} \;

Voila. Done.


NOTE: The only gotcha is that you've got two "before" spots and one "after". That is, in my case, you've got two .js instances to change. One looks for the .js files then the other replaces that .js with .ts.

find . -iname "*.js" -exec bash -c 'mv "$0" "${0%\.js}.ts"' {} \;
   first before ^                   second before ^   ^ one after
ruffin
  • 16,507
  • 9
  • 88
  • 138
5

You can also recursively rename files using the Finder.

  1. Browse to the folder where the tree of items are that need renaming.
  2. Switch the Finder window to "List" view.
  3. Expand all subfolders. This can be done using the keyboard shortcuts: ⌘-A (Cmd-A, select all), → (Right Arrow key, expand folder).
  4. Repeat the ⌘-A, → commands until all subfolders are open and all files are selected.
  5. Right-click on one of the selected files, and choose "Rename xx Items..."
  6. Use the "Rename Finder Items:" panel that opens to replace, add, or format text as desired.
  7. Click "Rename"
Steve
  • 365
  • 5
  • 10
  • Not really recursive, I have a folder containing 57,000 items across 1000's of subfolders so this would be far from ideal. – user2924019 Jul 20 '21 at 16:56
  • This simple thing worked for me..my directly structure was 6 or 7 deep and multiple bouts of command A + right arrow helped select everything and rename. thanks for this!! – Mihir Apr 24 '23 at 19:16
3

The answer to "how do I do X recursively on some file structure" is almost always to use find. In this case maybe also in combination with a for loop. The following example would recursively loop over the files matching the pattern foo in the current directory.

thedir=./
for file in $(find $dir -type f -name foo); do
    # rename $file here
done

Depending on your desired renaming scheme and how you wish to express the renaming, the body of the loop will have different renaming command(s).

Simply renaming every file named foo to bar will result in one file bar and that is not what people usually want. The $file variable contains the relative path to the file, so differents bar's will have different paths.