How can I bulk replace the prefix for many files?
I have a lot of files like
- TestSRConnectionContext.h
- TestSRConnectionContext.m
I would like to change all them to
- CLConnectionContext.h
- CLConnectionContext.m
How would I do this?
I'd say the simplest it to just use the rename
command which is common on many Linux distributions. There are two common versions of this command so check its man page to find which one you have:
## rename from Perl (default in Debian systems -- Ubuntu, Mint, ...)
rename 's/^TestSR/CL/' TestSR*
## rename from util-linux-ng (common in Fedora systems, Red Hat, CentOS, ...)
rename TestSR CL TestSR*
Using a TestSR*
shell wildcard as opposed to just *
helps in that it potentially passes far fewer file names to rename
and reduces the risk of running into a Argument list too long error, but also and more importantly avoids problems when there are file names that start with -
in the current working directory. It's particularly important for some of the perl
-based variants of rename
where not doing so introduces command injection vulnerabilities.
rename
exist in the wild. Check your local rename
documentation to figure out how to use yours.
rename "\<pre\>" "" *
-> Unterminated <> operator at (user-supplied code).
Commented
Jul 25, 2019 at 8:48
rename
is one of the perl variants, so you'd need rename 's/<pre>//' ./*'<pre>'*
to remove the first occurrence of <pre>
in each file name or rename 's/<pre>//g' ./*'<pre>'*
to remove all of them.
Commented
Dec 6, 2022 at 19:57
for name in TestSR*
do
newname=CL"$(echo "$name" | cut -c7-)"
mv "$name" "$newname"
done
This uses bash command substitution to remove the first 6 characters from the input filename via cut
, prepends CL
to the result, and stores that in $newname
. Then it renames the old name to the new name. This is performed on every file.
cut -c7-
specifies that only characters after index 7 should be returned from the input. 7-
is a range starting at index 7 with no end; that is, until the end of the line.
Previously, I had used cut -b7-
, but -c
should be used instead to handle character encodings that could have multiple bytes per character, like UTF-8.
Shell parameter expansion is enough for simple transformations like this. Command substitution is less efficient because of the need to spawn extra processes (for the command substitution itself and the cut
/sed
).
for f in TestSR*; do mv "$f" "CL${f#TestSR}"; done
TestSR*
finds the files, CL${f#TestSR}
will be the new name. The stuff after the #
will be substituted from the filename (f
).
Commented
Jun 1, 2018 at 11:31
Here's one way:
ls *.{h,m} | while read a; do n=CL$(echo $a | sed -e 's/^Test//'); mv $a $n; done
ls *.{h,m}
--> Find all files with .h or .m extensionn=CL
--> Add a CL prefix to the file namesed -e 's/^Test//'
--> Removes the Test prefix from the file namemv $a $n
--> Performs the renameYou can try with:
for i in TestSR*; do mv "$i" "${i/#TestSR/CL}"; done
See man bash
(section "Parameter Expansion") for details.
Should also work in ksh93 (where that syntax initially comes from) and zsh.
Well, it wasn't as hard as i though.
$ for f in TestSR*.m; do mv $f CL$(echo $f | cut -c7-); done;
$ for f in TestSR*.h; do mv $f CL$(echo $f | cut -c7-); done;
for f in TestSR*.[mh]
, for f in TestSR*.{m,h}
, for f in TestSR*.m TestSR*.h
.
If you need something more perlish you can do this
perl -e 'for(@ARGV) { rename($_, $n) if( ($n = $_ ) =~ s/^TestSR/CL/) }' *
I don't like any of the answers. My take is that you want a vanilla bash solution. So here you go!
Try this with echo first to see what is going to happen! Test your command before you use it on thousands of files!
Remove the './' is you want to put a prefix
find . -type f -exec bash -c 'echo prefix_${0#./}' {} \;
Remove the extension if you want to put something after it
find . -type f -exec bash -c 'echo ${0%.*}_suffix.${0##*.}' {} \;
Now to use this to rename just replace echo with mv $0
EXAMPLE: find . -type f -exec bash -c 'mv $0 1.${0#./}' {} \;
Inspired by: https://gist.github.com/larshaendler/723d2ec447c3c694b71dc6e3bc3aadc9
find . ... {} \;
you would be spawning separate bash subshells for each of the thousands of files you may find. That could make things very slow.
-exec
has never been really slow on huge tasks. And yes it could be faster. The question was "how can I bulk rename with prefix for many files" and not how to bulk rename in the fastest most efficient way. I would say: if you want something for a bash script, then use the for loop
method. If you want something you can type in from the top of your head to quickly operate in a terminal, learn the above structure. It's powerful and generic.
Commented
Apr 6, 2021 at 5:54
Obligatory answer using zsh's zmv:
autoload -Uz zmv # best in ~/.zshrc
zmv -n 'TestSR(*.[hm])' 'CL$1'
Remove the -n
(for dry-run) if happy.