I would like to change a file extension from *.txt to *.text. I tried using the basename command, but I'm having trouble on changing more than one file.

Here's my code:

files=`ls -1 *.txt`

for x in $files
    mv $x "`basename $files .txt`.text"

I'm getting this error:

basename: too many arguments Try basename --help' for more information

14 Answers 14


Straight from Greg's Wiki:

# Rename all *.txt to *.text
for f in *.txt; do 
    mv -- "$f" "${f%.txt}.text"

Also see the entry on why you shouldn't parse ls.

Edit: if you have to use basename, your syntax would be:

for f in *.txt; do
    mv -- "$f" "$(basename -- "$f" .txt).text"
  • 77
    There's some slightly advanced Bash syntax used here without explanation. I had to Google many things to understand this fully. To help others in my position, here are some references about globs (the *.txt syntax), the bare double dash --, and the use of the % symbol within the ${} construct. Until now I'd never encountered the latter two and hadn't known what was really happening with the former. – Mark Amery Sep 21 '14 at 12:32
  • 4
    Look at Matthias Braun's answer below - much simpler, accomplishes the same thing. – btk Mar 10 '15 at 14:38
  • 30
    One liner for f in *.txt; do mv -- "$f" "${f%.txt}.text"; done – Marçal Juan Oct 21 '15 at 14:35
  • 2
    @PéturIngiEgilsson on the second line? No, it's not required. – jasonwryan Jan 28 '17 at 8:33
  • 7
    what does the -- stand for? – Calculus Oct 28 '18 at 5:25

Here's how I change all the file extensions in the current directory on Debian.

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?)

  • 12
    Much simpler than the accepted answer, thank you! – btk Mar 10 '15 at 14:37
  • 18
    On Mac I was able to brew install rename and get this to work. – Monkpit Jul 19 '16 at 14:42
  • 4
    with fish shell you can do rename "s/oldExtension/newExtension/" **.txt to rename all *.txt recursively – Matěj Šmíd Feb 16 '17 at 13:03
  • 5
    This will fail for footxt.txt. Use this instead: unix.stackexchange.com/questions/19654/… – wisbucky Aug 1 '17 at 23:57
  • 7
    When your files don't have any extension and you want to add one: rename 's/$/.txt/' * – mkataja Aug 2 '17 at 7:21

A simple command, the rename from util-linux, will do that for you, it replace every occurences of "txt" to "text" in all file matching "*.txt":

rename txt text *.txt
  • 7
    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). – Gilles 'SO- stop being evil' Aug 29 '11 at 21:28
  • 18
    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. – evilsoup Nov 13 '13 at 15:12
  • @evilsoup That's the case for OSX, thx – Davi Lima Aug 17 '15 at 19:23
  • This also works on Cygwin (Windows) – andy Apr 12 '17 at 9:36
rename "s/oldExtension/newExtension/" *.txt

Above works fine but limited to current directory. Try the command below, which is flexible with sub-directories. It will rename all .txt files under directory structure with a new extension.

find . -type f -name "*.txt" -exec rename 's/\.txt$/.newext/' '{}' \;
  • 3
    rename can handle multiple files as argument, you can vastly speed things up by using + instead of \; if there are many such files – Anthon Feb 17 '15 at 8:57
  • Still, it is a useful option since "*.txt" can unroll to a large argument list unsupported by the shell. – Roman Shapovalov Oct 19 '16 at 13:14
  • 1
    "s/oldExtension/newExtension/" will fail for footxt.txt. Use this instead: unix.stackexchange.com/questions/19654/… – wisbucky Aug 1 '17 at 23:59

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
for f in *.txt
    [ -f "$f" ] && mv "$f" "${f%txt}text"

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.

for f in *.$1
    [ -f "$f" ] && mv -v "$f" "${f%$1}$2"

Example usage (assuming the name of the script is change-ext):

change-ext ext1 ext2
  • 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+. – Dennis Golomazov Feb 2 '12 at 10:40

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

Reason #53 to switch to zsh:

zmv '(*).txt' '$1.text'


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


  • -o: don't overwrite preexisting .newext
  • -v: verbose
  • -n: dry run

For more info, see man rename.ul or rename.ul -h.


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
  • 1
    Excellent bash-native script. worked great! for f in *.html; do mv -- "$f" "${f%.\*}.pug"; done – TamusJRoyce Jul 19 '19 at 17:54

This is what works for me:

find . -name '*.txt' -exec rename 's/\.txt$/.text/' \{} \;

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
  mv "$x" "`basename "$x" .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:

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' 

Not the answer you're looking for? Browse other questions tagged or ask your own question.