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
This script is ok, but you put $files (the full list) as parameter of basename instead of $x (the current file); the line should be mv $x «`basename $x .txt`.text» .
18 Answers 18
# Rename all *.txt to *.text for file in *.txt; do mv -- "$file" "$.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.
$ 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
For those who found this answer, be warned that if you mistakenly replace curly braces by parentheses in the «parameter expansion» syntax, it would remove your file. Don’t make the same mistake as I did (luckily i used a test dir).
Sharing my experience on using this answer. I used directly these lines in my shell script to rename the file. I just replaced for f in *.txt with my file path. cd /u1/test/myfiles for f in /u1/test/myfiles mv — «$f» «$(basename — «$f» .txt).text» done Above line worked well and rename the files correctly, but there is a catch. It won’t rename and copy at /u1/test/myfiles same location. It will copy at last location of your shell script. The path which we can see using pwd command. We just need to cd on path first.
One more tip — for quick little scripts like this, I often prepend echo in front of the command in the do statement . this has the result of outputting just what the script is about to do, without actually doing it.
Here’s how I change all the file extensions in the current directory on Debian or Ubuntu.
rename "s/oldExtension$/newExtension/" *.txt
On MacOS, user Monkpit reports that they were able to use brew install rename to get this to work.
with fish shell you can do rename «s/oldExtension/newExtension/» **.txt to rename all *.txt recursively
This would be ideal, rather than the chosen best answer. Anyway, note that different linux distros have different implementations of rename ; e.g. OpenSuse uses util-linux.rename and won’t work with regular-expressions (meh.. 😒), so not ideal for file-extension renaming.
I tried ➜ Comp ✗ rename «s/oldExtension$/jsx/» *.js but get zsh: no matches found: *.js edit, how do we make this recursive ?
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 changes the first occurrence, so better make that rename .txt .text , but this still won’t always work (e.g. it renames foo.txtx.bar.txt to foo.textx.bar.txt ).
It should be noted that not all systems have the same version of rename; on Debian and friends, the rename command is actually perl-rename and uses perl regexes. For that, the equivalent command would be: rename ‘s/.txt/.text/’ *.txt . People should check the man rename on their system to find out which one they have.
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/' '<>' \;
rename can handle multiple files as argument, you can vastly speed things up by using + instead of \; if there are many such files
Still, it is a useful option since «*.txt» can unroll to a large argument list unsupported by the shell.
«s/oldExtension/newExtension/» will fail for footxt.txt . Use this instead: unix.stackexchange.com/questions/19654/…
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:
For Mac, I have to run this command «autoload -U zmv». Otherwise, I am getting «zsh: command not found: zmv»
Running zsh 5.8 (x86_64-ubuntu-linux-gnu) . No zmv command. is it supposed to be installed by default?
@Olsgaard 1) It’s not a command actually, it’s a ZSH function. 2) As was mentioned above, you need to execute autoload in order to use zmv (read this: «What does autoload do in zsh?»). Also here is a nice tutorial: «How to use zmv — Z Shell’s super-smart file renamer».
for f in *.txt do [ -f "$f" ] && mv "$f" "$text" done
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" "$$2" done
Example usage (assuming the name of the script is change-ext ):
To change extensions of files in directories recursively, replace the second line ( for. ) with two lines: shopt -s globstar and for f in **/*.$1 . Requires Bash 4+.
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" $.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
For more info, see man rename.ul or rename.ul -h .
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" "$.text"; done
This does not change the extension. It adds to it. You have to use basename to extract the base part of the filename.
This is what works for me:
find . -name '*.txt' -exec rename 's/\.txt$/.text/' \<> \;
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 :
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.:
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
A very simple solution, based on the old good xargs, to add a suffix/extension to filenames corresponding to :
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 *.$; do mv -v -- "$file" "$(echo "$file" | sed "s/\.$$/.$/")" done >
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)
How can I change the extension of files of a type using «find» with Bash? [duplicate]
The user inputs a file type they are looking for; it is stored in $arg1 ; the file type they would like to change them is stored as $arg2 . I’m able to find what I’m looking for, but I’m unsure of how to keep the filename the same but just change the type. ie., file1.txt into file1.log .
find . -type f -iname "*.$arg1" -exec mv <> \;
@Kenavoz The alleged duplicate deals with files in one directory only. I’m pretty sure there is a duplicate somewhere, but I can’t find it right now.
4 Answers 4
To enable the full power of shell parameter expansions, you can call bash -c in your exec action:
find . -type f -iname "*.$arg1" \ -exec bash -c 'echo mv "$1" "$"' _ <> "$arg2" \;
We add <> and «$arg2» as a parameters to bash -c , so they become accessible within the command as $0 and $1 . $ removes the extension, to be replaced by whatever $arg2 expands to.
As it is, the command just prints the mv commands it would execute; to actually rename the files, the echo has to be removed.
The quoting is relevant: the argument to bash -c is in single quotes to prevent $0 and $1 from being expanded prematurely, and the two arguments to mv , and arg2 are also quoted to deal with file names with spaces in them.
Could there be ref. to what the _ is in this case? is it this parameter from the shell expansions page?
Combining the find -exec bash idea with the bash loop idea, you can use the + terminator on the -exec to tell find to pass multiple filenames to a single invocation of the bash command. Pass the new type as the first argument — which shows up in $0 and so is conveniently skipped by a for loop over the rest of the command-line arguments — and you have a pretty efficient solution:
find . -type f -iname "*.$arg1" -exec bash -c \ 'for arg; do mv "$arg" "$.$0"; done' "$arg2" <> +
Alternatively, if you have either version of the Linux rename command, you can use that. The Perl one (a.k.a. prename , installed by default on Ubuntu and other Debian-based distributions; also available for OS X from Homebrew via brew install rename ) can be used like this:
find . -type f -iname "*.$arg1" -exec rename 's/\Q'"$arg1"'\E$/'"$arg2"'/' <> +
That looks a bit ugly, but it’s really just the s/old/new/ substitution command familiar from many UNIX tools. The \Q and \E around $arg1 keep any weird characters inside the suffix from being interpreted as regular expression metacharacters that might match something unexpected; the $ after the \E makes sure the pattern only matches at the end of the filename.
The pattern-based version installed by default on Red Hat-based Linux distros (Fedora, CentOS, etc) is simpler:
find . -type f -iname "*.$arg1" -exec rename ".$arg1" ".$arg2" <> +
but it’s also dumber: if you rename .com .exe stackoverflow.com_scanner.com , you’ll get a file named stackoverflow.exe_scanner.exe .