0. Aiming for generic solutions 1
For the convenience of my future self and other readers, I will focus
on generic solutions.
For simplicity I will assume that there are no quotation marks
– no double quotes ("
) and no single quotes ('
) –
in any file/directory name.
2
As a first example, I will show how to rename files with the pattern
o l d prefix file *.*
to
n e w prefix file *.*
,
thus renaming a prefix even if there are
spaces in the file names.
I am also interested in renaming directories, and will therefore
consider renaming the directories
o l d prefix, dir 1 _
and o l d prefix, dir 2 _
to
n e w prefix, dir 1 _
and n e w prefix, dir 2 _
respectively.
Secondly, I will show how to replace a suffix. As an example, the files
file 1, o l d suffix
and file 2, o l d suffix
are renamed to
file 1, n e w suffix
and file 2, n e w suffix
respectively.
Again, I want to see how this affects (or not affects) directories.
I will therefore also check to see whether the directories
dir 1., o l d suffix
and dir 2., o l d suffix
are renamed to
dir 1., n e w suffix
and dir 2., n e w suffix
respectively.
3
All of the above files and directories are available in a zip file:
http://user.it.uu.se/~hesc0353/Files-&-dirs-SEunix-A690768.zip.
4
Note.
The occurrence of echo
in the solutions below means no renaming will
take place.
This is an established technique to preview the renaming before you
decide to go through with it.
Once the preview looks OK, just remove echo
and run the command
again to perform the actual renaming.
5
1. A generic solution using a for-loop
Renaming a prefix means replacing o l d prefix
by n e w prefix
:
6
for f in "o l d prefix"*; do\
echo mv "$f" "n e w prefix""${f#o l d prefix}"; done
where I have chosen to search for files that start with o l d pref
.
To rename a suffix, try :
for f in *"l d suffix"; do\
echo mv "$f" "${f%o l d suffix}""n e w suffix"; done
Does the renaming affect only directories, only files, or both
directories and files?
– This method changes the names of both files and directories.
If this is not what you want, try the find ... -type ... -exec
method in the next section.
Will files/directories in sub-directories also be renamed?
– No. Using one of the above commands as a template will not affect
files/directories in any sub-directories.
You will have to purposely tweak the method quite a bit if you want to
do that.
See for example the first solution in this answer.
2. A generic solution using find ... -exec mv
The find ... -exec mv
method below gives you the option to rename
files only or directories only – through the -type
flag.
It can also be tweaked to control how deep in the sub-directory tree
the command should search for files or directories – through the
-maxdepth
flag.
Rename a prefix
The following command replaces o l d prefix
by n e w prefix
, but for
directories only.
Each file could be renamed in a separate sub-shell, but by having a
for-loop inside -exec
(and closing -exec
with sh {} +
instead
of {} \;
) the command runs more efficiently :
find . -maxdepth 1 -type d -name "o l d pr*" -exec sh -c\
'for n; do echo mv "${n#./}" "n e w pre${n#./o l d pre}"; done' sh {} +
If you want to rename only files, simply use -type f
instead of
-type d
(easy to remember: f
for files, d
for directories).
Rename a suffix
Similarly, here is how to replace a suffix in a bulk of files:
find . -maxdepth 1 -type f -name "*d suffix" -exec sh -c\
'for n; do echo mv "$n" "${n%o l d suffix}n e w suffix"; done' sh {} +
Explanations
find .
Search in the current directory and its sub-directories.
-maxdepth 1
Do not search in any of the sub-directories.
-type f
Find files only (exclude directories).
-name "*d suffix"
Search only files that end with d suffix
.
-exec sh -c '...'
Execute in sub-shells, allows command
substitution, $(...)
.
-exec sh -c 'for n; do mv ...' sh {} +
is more efficient than
-exec sh -c 'mv ...' {} \;
which otherwise does the same thing.
7
{}
holds whatever find
finds.
echo
outputs the result of the command – when you are happy with
the outcome,
remove echo
and run the command again.
mv "sou rce" "dest in"
performs the actual renaming,
replacing the old name sou rce
with the new name dest in
.
%
See POSIX shells pattern filtering.
done
ends the for-loop.
sh {} +
As already indicated, sh {} +
closes the -exec sh -c
clause.
Rename both files and directories
To deliberately rename both files and directories in one go,
just leave out -type
:
find . -maxdepth 1 -name "o l d pr*" -exec sh -c\
'for n; do echo mv "${n#./}" "n e w pre${n#./o l d pre}"; done' sh {} +
Will files/directories in sub-directories also be renamed?
The -maxdepth 1
clause ensures that no files/directories in the
sub-directory tree will be affected.
If you deliberately want to rename a suffix for all files in all
sub-directories, here is an example :
find . -type f -name "*d suffix" -exec sh -c\
'for n; do echo mv "$n" "${n%o l d suffix}n e w suffix"; done' sh {} +
A word of warning.
This method will not work for renaming a prefix in many different
sub-directories in one command.
3. A solution using Perl rename
This section is devoted to those of you who have
Perl rename by Larry Wall
installed on your system (or know how to install it).
8
I like the prudent practice of renaming the rename
Perl-script to
prename
so as to avoid confusing it with the
Linux rename.
Does the method work for prefixes only, for suffixes only, or does
it work irrespectively?
The fact that you don't need to bother whether you are renaming a
prefix or a suffix makes Perl rename easy to use.
For example, to replace o l d prefix
with n e w prefix
, do :
prename -n 's/o l d prefix/n e w prefix/' ./"o l d p"*
where the -n
flag produces a preview.
Once the result is what you want, simply remove the -n
flag and run
the command again.
Does the renaming affect only directories, only files, or both
directories and files?
As with the for-loop method of section 1, running prename
will
rename both directories and files.
However, you can choose to rename only files (exclude directories) by
adding if -f
at the end of the rename string, like so :
9
prename -n 's/o l d prefix/n e w prefix/ if -f' ./"o l d p"*
If you want to rename only directories (exclude files) use if !-f
:
prename -n 's/o l d prefix/n e w prefix/ if !-f' ./"o l d p"*
Will files/directories in sub-directories also be renamed?
I have found that – when using forward slash (/
) as delimiter –
Perl rename alone will rename files/directories in the current working
directory only and not in any of the sub-directories.
If you use some other character as delimiter, you can actually
use prename
to rename files/directories in the sub-directory tree.
The second solution of this answer is an example of such a rather
unorthodox practice.
A more normal practice would be to combine find ... -execdir
with
prename
.
For such an example, see
this answer.
4. Other good alternatives for renaming files or directories
In case you don't like any of the above methods, consider using
sed
or awk
(possibly in combination with xargs
).
References
1 When testing the commands/solutions provided in this
answer, I have neither used genuine Linux nor genuine Unix, but MSYS2
on Windows 10, which can be described as an emulated arch-Linux
running on Microsoft Windows. Of course, a viable alternative for
Windows users like myself would be to use
Windows Subsystem for Linux (WSL).
I use MSYS2 because I find it convenient and easy to use.
2 If in doubt, before considering any renaming whatsoever,
check the results of
the commands ls *\"*
and ls *\'*
.
Or better still: find . -name '*"*'
and find . -name "*'*"
.
To have quotation mark(s) in file/directory names is definitely to ask
for trouble, I think.
This answer lists what
characters are forbidden in file/directory names.
– For Linux/Unix, there is only one forbidden character: forward slash
(/
).
3 I will not aim for a solution that changes both a prefix
and a suffix in one go, as asked for in the question
Renaming files with Bash, removing prefix and suffix.
(If you need to rename both a prefix and a suffix, I would suggest
replacing one of them in one command, and then replace the other one
in another command.)
4 I encourage the reader to download and unzip the zip file
to some (empty) directory, and then try out all the commands I provide
in this answer.
5 For files and directories containing spaces, the preview
may look wrong because it is missing the surrounding double-quotes
("
).
Removing echo
will still make the command run correctly.
(Don't take the preview too literally!)
6 I use a backslash (\
) to line-break long commands so
that you as a reader won't need to scroll horizontally.
When you construct a concrete example yourself, I recommend removing
the backslash and writing the whole command in just one line.
7 To understand more about the difference between {} +
and
{} \;
, see for example
How to terminate the shell commands invoked by -exec
(and its references).
8 Try typing rename
or prename
in your terminal (and hit
Enter).
If Perl rename is installed, expect to see
Usage: rename [-v] [-n] [-f] perlexpr [filenames]
or something
similar – where the word perlexpr
is included.
If it is not installed, expect to see something like
bash: prename: command not found
.
Another thing you could try is which prename
.
9 This comment says the
if -f
clause will also rename symbolic links.
It suggests using if (-f && ! -l)
instead, if symlinks are not to
be renamed.