How do I change the extension of multiple files?
I would like to change a file extension from *.txt
to *.text
. I tried using the basename
command, but I’m having trouble changing more than one file.
Here’s my code:
files=`ls -1 *.txt`
for x in $files
do
mv $x "`basename $files .txt`.text"
done
I’m getting this error:
basename: too many arguments Try basename –help’ for more information
for f in *.txt
do
[ -f "$f" ] && mv "$f" "${f%txt}text"
done
Straight from Greg’s Wiki:
# Rename all *.txt to *.text
for file in *.txt; do
mv -- "$file" "${file%.txt}.text"
done
*.txt
is a globbing pattern, using *
as a wildcard to match any string. *.txt
matches all filenames ending with ‘.txt’.
--
marks the end of the option list. This avoids issues with filenames starting with hyphens.
${file%.txt}
is a parameter expansion, replaced by the value of the file
variable with .txt
removed from the end.
Also see the entry on why you shouldn’t parse ls
.
If you have to use basename
, your syntax would be:
for file in *.txt; do
mv -- "$file" "$(basename -- "$file" .txt).text"
done
A simple command, rename
from util-linux
, will do that for you. It replaces every occurrence of "txt" with "text" in all files matching "*.txt":
rename txt text *.txt
Based on the @Prince John Wesley answer, here is a simple bash script for changing all extensions of files in the current directory from ext1
to ext2
. Also outputs names of the files being renamed.
#!/bin/bash
for f in *.$1
do
[ -f "$f" ] && mv -v "$f" "${f%$1}$2"
done
Example usage (assuming the name of the script is change-ext
):
change-ext ext1 ext2
let’s say your files are scattered in various directory,
Assuming that dirx is your parent directory, this can do the job using find:
for f in `find /dirx -iname '*.txt' -type f -print`;do mv "$f" ${f%.txt}.text; done
Here’s how I change all the file extensions in the current directory on Debian or Ubuntu.
rename "s/oldExtension$/newExtension/" *.txt
(This is the Perl rename
command, not the util-linux
one. See Why is the rename utility on Debian/Ubuntu different than the one on other distributions, like CentOS?)
On MacOS, user Monkpit reports that they were able to use brew install rename
to get this to work.
rename "s/oldExtension/newExtension/" *.txt
Above works fine but is limited to the current directory. Try the command below, which is flexible with sub-directories. It will rename all .txt
files under the directory structure with a new extension.
find . -type f -name "*.txt" -exec rename 's/.txt$/.newext/' '{}' ;
In case you want to know what went wrong in your version: You used $files
instead of $x
in the basename command. So this should work (untested, though):
for x in *.txt
do
mv "$x" "`basename "$x" .txt`.text"
done
This is what works for me:
find . -name '*.txt' -exec rename 's/.txt$/.text/' {} ;
The answers here referencing s/oldExtension/newExtension/
are wrong. If you use s/txt/text/
, you would convert footxt.txt
to footext.txt
, which is not what you want. Even if you use s/.txt/.text/
, that would convert footxt.txt
to fo.text.txt
.
You have to use .
to match the period (.
will match any character). And the trailing $
to match the end of the line. Only this will properly match the extension.
rename 's/.txt$/.text/' *.txt
rename 's/.old$/.new/' *.old
Reason #53 to switch to zsh:
zmv '(*).txt' '$1.text'
When you
do not have an extension for the source files
and target extension is .text you would do it this way –
for f in *; do mv -- "$f" "${f%.*}.text"; done
On Ubuntu 18.04, the util-linux rename command is available as rename.ul
. This worked for me:
rename.ul -o -v .oldext .newext *.oldext
Options:
- -o: don’t overwrite preexisting .newext
- -v: verbose
- -n: dry run
For more info, see man rename.ul
or rename.ul -h
.
Mmv (available in the main distributions repositories) is also very useful for renaming. Give the patterns in quotes and each glob element can be reproduced by #N
:
mmv '*.txt' '#1.text'
Some more interesting, neat examples in the manual page:
Rename files ending in
.html.en
,.html.de
, etc. to ending in.en.html
,.de.html
, etc.:mmv '*.html.??' '#1.#2#3.html'
Rename music files from
<track no.> - <interpreter> - <song title>.ogg
to<interpreter> - <track no.> - <song title>.ogg
:mmv '* - * - *.ogg' '#2 - #1 - #3.ogg'
Nobody has shown using shell parameter expansion, which is the most basic, probably shell compliant, (and I believe most readable) way of doing this.
# by removing suffix
for f in *.txt; do echo "$f" "${f%txt}text"; done
# by substitution
for f in *.txt; do echo "$f" "${f/%txt/text}"; done
# simply edit mv for echo after testing
# % searched for shortest string, %% is 'greedy' would search for longest
# if you have a basename, use basename*.txt
https://www.debuntu.org/how-to-bash-parameter-expansion-and-string-manipulation/
https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
A very simple solution, based on the old good xargs, to add a suffix/extension to filenames corresponding to <pattern>:
ls <pattern> | xargs -i mv {} {}.<suffix>
for f in *.old_ext; do
mv -- "$f" "$(echo "$f" | sed "s/.old_ext$/.new_ext/")"
done
I am posting this because I have not seen another answer that details this method and why it could potentially be superior to the accepted answer. You can go here to see all the reasons why this method is better than all the other answers, but basically, the accepted answer is not POSIX-compliant since it relies on bash
parameter expansion.
I realize OP tagged bash
specifically, and for that reason, the accepted answer will work just fine, but if you wanted to use this command in a shell script on a system using sh
or dash
, you could use the version I posted above, which utilizes any version of sed
(GNU/BSD/anything), and will work just fine, while not relying specifically on bash
itself.
I actually wrote that command for this specific use case, which I encounter frequently and have been using it as a bash
/zsh
function for a long time, which I named chext
:
chext() {
old_ext="$1"
new_ext="$2"
for file in *.${old_ext}; do
mv -v -- "$file" "$(echo "$file" | sed "s/.${old_ext}$/.${new_ext}/")"
done
}
Usage:
chext [old_extension] [new_extension]
Recently, I had the need to use it on a different computer, and instead of just a one-off copy/paste, I actually added it to my main set of portable commands, phxutils.
After publishing chext
in phxutils
, I did a quick search to see if there was actually a better way of accomplishing what I set out to do, and after browsing through all of these answers, I still think my way is the best and most universal (at least for current directory extension replacement — for other methods, I would use one of the find
variations)
For a slightly different take, in the case of a directory under git version control, you might consider:
# Rename all *.txt to *.text
for file in *.txt; do
git mv -- "$file" "${file%.txt}.text"
done
In my case this avoided having git think the files were simply deleted.