Notes

Be careful not use reserved variables like PATH otherwise things can break. Use file, FILE, p or PATH.

Be careful about globbing on hidden directories - as you can break .git.

Change extension

The find command is probably more efficient and could let you run multiple commands, but using the for loop has more familiar syntax. There may be trade-offs for each when it comes to renaming (or moving) a directory, since it will affect future traversing steps in the same sequence. Using find might give more precision on the filename and type compared to using a * glob with for and having to use if statements in the loop to do checks.

Using the find command

Note that the find command will work recursively by default.

Optionally add the -depth command. Note from the docs:

-depth Process each directory’s contents before the directory itself. The -delete action also implies -depth.

You can omit -name and value. Note that the * glob is necessary there otherwise you’ll get no results.

Here is the general form to search and perform an action. The part at the end is hard to remember but is needed. Additionally add -type f for just files.

$ find PATH -name SEARCH -exec bash -c 'COMMAND' '{}' \;

Renaming:

$ find . -exec bash -c 'mv "$1" "RULE"' - '{}' \;

source

Rename .foo to .bar.

$ find . -name '*.foo' -exec bash -c 'mv "$1" "${1%.foo}".bar' - '{}' \;

AskUbuntu

Replace underscores with dashes. To avoid mv errors on renaming a file to the same name as before, we only match on files with underscores in them.

$ find . -name '*_*' '-exec sh -c 'mv "$1" ${1//_/-}' - '{}' \;

You use the rename tool.

$ find . -name '*.foo' -exec rename 's/\.foo$/.bar/' '{}' \;

Preview

Print only. Use echo instead of moving.

You need $0 instead of $1. And the hyphen at the end can be removed.

$ find . -name README.md -exec sh -c 'echo $0' '{}' \;
ci-cd/circle-ci/README.md
ci-cd/README.md
ci-cd/netlify/README.md
...

Git

In a git repo, you can use git mv:

$ find . -name "*.foo" -exec bash -c 'git mv "$1" "${1%.foo}".bar' - '{}' \;

For loop

Use globstar to get recursive globs (**) - only Bash 4+ or ZSH.

for file in PATH/**/*.foo; do
  mv "$file" "${file%.foo}.bar"
done

howtogeek.com

Here we replace extension .foo with .bar using a for loop:

for file in *.foo; do mv -- "$file" "${file%.foo}.bar"

Replace space with underscore in certain file types, using parameter expansion.

for P in *.doc *.mp3 *.wav *.txt; do
  mv -- "$P" "${P// /_}"
done

Note the replacement syntax is different to that of sed. (Also, the double dash will skip files that are unchanged I think. Or use mv -i?)

Here we rename README.md to index.md (rather than changing extension):

for P in **/README.md; do git mv -v "$P" "${P//README.md/index.md}"; done

Use -n|--dry-run flag to preview first (Linux only). Using the -v|--verbose flag is implied with that.

Replace an underscore with a dash in file and directory names - using globstar.

for P in **/*_*; do
  git mv --verbose "$P" "${P//_/-}"
done

Notes:

  • This ignores hidden files. Important - ignore .git. Also note that files in .github like templates might only work using dashes.
  • That will ignore the top-level, so be sure to run also for just *_*.
  • That will give an error on directory paths which have an underscore in multiple levels, as it will try to name the same item multiple times in one run. So you have to run the command a second time to catch those. If you have two directory levels and a file inside all with underscores, you’ll have to run the command 3 times, which is okay.
  • The above can be followed by a search in the IDE with regex pattern. Note that cases should be checked before replacing. Pattern: \w+_\w+\.md

Rename tool

Install the rename utility.

sudo apt install rename

Rename file extension for a batch of files.

rename 's/.foo/.bar/' *.foo