Rename files and directories recursively under ubuntu /bash
Solution 1
Try doing this (require bash --version
>= 4):
shopt -s globstar
rename -n 's/special/regular/' **
Remove the -n
switch when your tests are OK
There are other tools with the same name which may or may not be able to do this, so be careful.
If you run the following command (GNU
)
$ file "$(readlink -f "$(type -p rename)")"
and you have a result like
.../rename: Perl script, ASCII text executable
and not containing:
ELF
then this seems to be the right tool =)
If not, to make it the default (usually already the case) on Debian
and derivative like Ubuntu
:
$ sudo update-alternatives --set rename /path/to/rename
(replace /path/to/rename
to the path of your perl's rename
command.
If you don't have this command, search your package manager to install it or do it manually
Last but not least, this tool was originally written by Larry Wall, the Perl's dad.
Solution 2
A solution using find
:
To rename files only:
find /your/target/path/ -type f -exec rename 's/special/regular/' '{}' \;
To rename directories only:
find /your/target/path/ -type d -execdir rename 's/special/regular/' '{}' \+
To rename both files and directories:
find /your/target/path/ -execdir rename 's/special/regular/' '{}' \+
Solution 3
If you don't mind installing another tool, then you can use rnm:
rnm -rs '/special/regular/g' -dp -1 *
It will go through all directories/sub-directories (because of -dp -1
) and replace special with regular in their names.
Solution 4
@speakr's answer was the clue for me.
If using -execdir to transform both files and directories, you'll also want to remove -type f
from the example shown. To spell it out, use:
find /your/target/path/ -execdir rename 's/special/regular/' '{}' \+
Also, consider adding g
(global) flag to the regex if you want to replace all occurrences of special
with regular
in a given filename and not just the first occurrence. For example:
find /your/target/path/ -execdir rename 's/special/regular/g' '{}' \+
will transform special-special.jpg
to regular-regular.jpg
. Without the global flag, you'll end up with regular-special.jpg
.
FYI: GNU Rename is not installed by default on Mac OSX. If you are using the Homebrew package manager, brew install rename
will remedy this.
Solution 5
Here is another approach which is more portable and does not rely on the rename
command (since it may require different parameters depending on the distros).
It renames files and directories recursively:
find . -depth -name "*special*" | \
while IFS= read -r ent; do mv $ent ${ent%special*}regular${ent##*special}; done
What it does
- use find with
-depth
parameter to reorder the results by performing a depth-first traversal (i.e. all entries in a directory are displayed before the directory itself). - do pattern substitutions to only modifiy the last occurence of regular in the path.
That way the files are modified first and then each parent directory.
Example
Giving the following tree:
├── aa-special-aa
│ └── bb-special
│ ├── special-cc
│ ├── special-dd
│ └── Special-ee
└── special-00
It generate those mv
commands in that particular order:
mv ./aa-special-aa/bb-special/special-cc ./aa-special-aa/bb-special/regular-cc
mv ./aa-special-aa/bb-special/special-dd ./aa-special-aa/bb-special/regular-dd
mv ./aa-special-aa/bb-special ./aa-special-aa/bb-regular
mv ./aa-special-aa ./aa-regular-aa
mv ./special-00 ./regular-00
To obtain the following tree:
├── aa-regular-aa
│ └── bb-regular
│ ├── regular-cc
│ ├── regular-dd
│ └── Special-ee
└── regular-00
Tom
Updated on July 05, 2022Comments
-
Tom almost 2 years
I want to rename all files and directories that contain the word "special" to "regular". It should maintain case sensitivity so "Special" won't become "regular".
How can i do this in bash recursively?
-
Tom about 11 yearswhat is the
shopt -s globstar
for? -
Gilles Quenot about 11 yearsIt enables the bash feature
**
(stands for recursive) maybe already enabled. -
Tom about 11 yearsGNU bash, version 4.2.24(1)-release
-
Gilles Quenot about 11 years
-
Dmitry Gonchar about 9 years@speakr, Great solution, thanks! Could you provide more details about what it is doing, especially this part " '{}' \ "? Quick goggling did not help, so I thought that this information may be useful for others as well.
-
speakr about 9 years@DmitryGonchar Just take a look at
-exec command ;
in the find manpage. -
killjoy almost 9 yearsThe chat window you linked to is borked..I just try this and I got this response: rename: invalid option -- 'n' version: [##@localhost Staticality]$ bash --version GNU bash, version 4.2.46(1)-release (x86_64-redhat-linux-gnu)
-
Dennis over 8 yearsI have about 1700 files to rename in directories in my home directory. That seems too many for this to work, that actually was the text in the error message, too many arguments.
-
Dennis over 8 yearsI tried this with the -n flag (not ready to experiment with permanet results yet, and got this error: Replacement list is longer than search list at (eval 1) line 1. My actual command invoked was: find $HOME -type f -exec prename -n 'y/ /-_-/' '{}' \;
-
Gilles Quenot over 8 years@killjoy: you have to install perl's rename. (sometimes named
prename
or install the module:cpan File::Rename
@Dennis: use find :find . -exec rename 's/special/regular/' {} +
-
user1 almost 8 yearsDoes it work recursively in the current directory or globally? In other words, is the path "." or "/"?
-
speakr over 7 years@ThiloSchulz It does, but you have to use
find … -execdir … '{}' +
instead of… '{}' \;
. Clarification added. -
user658182 over 7 years@speakr I'm not getting the results I'm expecting for replacing directories, and I'm not sure I'm understanding the 'find ... -execdir suggestion. Do I change type f to type d? Can you please write out the full working command for directories in your answer please?
-
LokMac about 7 yearsIf anyone can clarify what is done to action the command for both files and directories in a single pass that would be appreciated too, and I'll update this answer with the command.
-
LokMac about 7 yearsIs this installed from Homebrew?
-
Jahid about 7 years@LokMac : I don't think it's available in homebrew (I only package binary for debian based OS), If you have automake, you can install it in any Unix system (Instructions are in the readme).
-
AnnanFay almost 7 yearsFor me this causes
-bash: /usr/bin/rename: Argument list too long
-
Gilles Quenot almost 7 yearsSo use
find /dir -exec rename 's///' {} +
-
Black almost 7 yearsGreat tool! Worked for me!
-
speakr over 6 years@user658182 Yes, use
-type d
combined with-execdir
for directories only. -
Brannon over 6 yearsYou may have to run the rename command multiple times in a row: run it until it doesn't report moved files.
-
Gilles Quenot over 6 years-_- Are you sure you have perl's rename before 'downvoting' ?
-
Ferroao over 6 yearsin order to avoid errors use for i in **; do rename 's/<toreplace>/<replacewith>/' "$i"; done after globstar
-
Rui Seixas Monteiro over 6 yearsIn case of many files, take advantage of regex in find, for the last example above you use:
find /your/target/path/ -regex '.*special.*' -execdir rename 's/special/regular/' '{}' \+
, otherwise it will be quite slow! -
Romain Vincent over 6 yearsPeople trying to rename a pattern occuring multiple times in the same name should add the 'g' flag at the end of the substitution command:
's/old/new/g'
-
inetphantom about 5 yearswill this correclty handle including folders? e.g.
/special/regular/regular-specilal.jpeg
-
Cmag about 4 yearsgreat, thank you. However, does not seem to do the job
recursively
on OSX.... -
Dax over 2 yearsThanks a lot for this explanation, perfect fit my needs.
-
MrR about 2 yearsBut it doesn't do a depth first traversal so it's open to problems with parent directories haven't been renamed first.