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

12 Answers 12


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"
  • 50
    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
  • 3
    Look at Matthias Braun's answer below - much simpler, accomplishes the same thing. – btk Mar 10 '15 at 14:38
  • 11
    One liner for f in *.txt; do mv -- "$f" "${f%.txt}.text"; done – Marçal Juan Oct 21 '15 at 14:35
  • 1
    You have a missing ; after text". – Pétur Ingi Egilsson Jan 28 '17 at 8:26
  • 2
    @PéturIngiEgilsson on the second line? No, it's not required. – jasonwryan Jan 28 '17 at 8:33

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

  • 10
    Much simpler than the accepted answer, thank you! – btk Mar 10 '15 at 14:37
  • 9
    On Mac I was able to brew install rename and get this to work. – Monkpit Jul 19 '16 at 14:42
  • 3
    with fish shell you can do rename "s/oldExtension/newExtension/" **.txt to rename all *.txt recursively – Palmstrom Feb 16 '17 at 13:03
  • 3
    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
  • 4
    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
  • 13
    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 . -name "*.txt" -exec rename 's/.txt$/.newext/' {} \;
  • 2
    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

Reason #53 to switch to zsh:

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


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

This is what works for me:

find . -name '*.txt' -exec rename 's/\.txt$/.text/' \{} \;
  • 1
    PLEASE add a . before the "text" – danielmhanover Jul 3 '17 at 7:10

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 at 17:54

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"

protected by Community Feb 18 '15 at 11:39

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

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