I want to recursively iterate through a directory and change the extension of all files of a certain extension, say .t1
to .t2
. What is the bash command for doing this?

- 761,203
- 64
- 569
- 643

- 6,477
- 14
- 53
- 76
-
14imho this is not a duplicate question - the other question is not recursive – shonky linux user Jun 24 '16 at 04:37
-
Related: https://askubuntu.com/questions/35922/how-do-i-change-extension-of-multiple-files-recursively-from-the-command-line – Ciro Santilli OurBigBook.com Oct 29 '20 at 10:45
-
@AmalAntony : If you don't have `rename`, write a shell script, which renames a single file (trivial to do in your simple case), and then use `find` to apply this script to all files with the offending extension. – user1934428 Nov 23 '20 at 14:20
6 Answers
Use:
find . -name "*.t1" -exec bash -c 'mv "$1" "${1%.t1}".t2' - '{}' +
If you have rename
available then use one of these:
find . -name '*.t1' -exec rename .t1 .t2 {} +
find . -name "*.t1" -exec rename 's/\.t1$/.t2/' '{}' +

- 26,865
- 29
- 124
- 202

- 761,203
- 64
- 569
- 643
-
20
-
2(My version of `rename` doesn't allow the sed style substitution expression. Gotta love Linux. I used to have to install TotalCommander for Windows to do stuff like this.) – Aaron Blenkush Dec 09 '15 at 00:03
-
1
-
1If you have the extensions in variables, you cannot use single quotes here. `find . -name "*.$ext1" -exec bash -c "mv \"\$1\" \"\$(sed 's/\\.$ext1\$/.$ext2/' <<< \"\$1\")\"" - {} \;` should work instead, though this is much more involved than I would like it to be. – tripleee Sep 11 '16 at 13:58
-
7In case anyone is wondering what the `"${1%.t1}".t2` part does, like I did: It uses bash string manipulation to do the following: 1/ Take the first positional parameter `$1` and truncate the `.t1` string literal from its end (percentage sign `%` operator). 2/ Append the `.t2` string literal to the result. – Zack Jul 21 '17 at 01:55
-
2The rename didn't work for me in OSX, but the bash version is awesome b/c I just added 'git' in front of mv and now git is happy :-D – bdombro Sep 21 '18 at 13:37
-
5
-
1
-
1Another form would be `mv "$1" "${1/.t1/.t2}"` this uses find and replace, but your filename need to have only one ".t1". – axell-brendow May 28 '20 at 14:33
-
2@AaronBlenkush In Windows I would use PowerShell for this: `gci -r *.t1 | foreach { $_ -replace ".t1", ".t2" }` these commands will print the filenames renamed. – axell-brendow May 28 '20 at 14:40
-
2
-
14Delimiter argument should be `;` instead of `+` if renaming all at once is required like this `find . -name "*.t1" -exec bash -c 'mv "$1" "${1%.t1}".t2' - '{}' \;`. Otherwise with the `+` only one file will be renamed at a time. [Ref](https://www.baeldung.com/linux/find-exec-command) – S.aad Jan 20 '22 at 14:09
None of the suggested solutions worked for me on a fresh install of debian 11. This should work on any Posix/MacOS
find ./ -depth -name "*.t1" -exec sh -c 'mv "$1" "${1%.t1}.t2"' _ {} \;
All credits to: https://askubuntu.com/questions/35922/how-do-i-change-extension-of-multiple-files-recursively-from-the-command-line

- 1,107
- 1
- 10
- 22
-
Thanks for this. The rename stuff in the selected answer doesn't work on Ubuntu 20 – CaptainCodeman Nov 06 '22 at 21:17
-
This is the only one worked for me. None of the accepted answer methods worked. I am on Ubuntu so probably a Debian/CentOs whatever thing. cheers – Neo Nov 17 '22 at 19:35
-
1
If your version of bash
supports the globstar
option (version 4 or later):
shopt -s globstar
for f in **/*.t1; do
mv "$f" "${f%.t1}.t2"
done

- 497,756
- 71
- 530
- 681
I would do this way in bash :
for i in $(ls *.t1);
do
mv "$i" "${i%.t1}.t2"
done
EDIT : my mistake : it's not recursive, here is my way for recursive changing filename :
for i in $(find `pwd` -name "*.t1");
do
mv "$i" "${i%.t1}.t2"
done

- 21,103
- 9
- 64
- 78
-
10[Don't parse ls](http://mywiki.wooledge.org/ParsingLs), and see the same page for why your `find` syntax is bad. Also, make sure you [quote your variables](http://mywiki.wooledge.org/BashGuide/Practices#Quoting) – Reinstate Monica Please Feb 24 '14 at 12:26
Or you can simply install the mmv
command and do:
mmv '*.t1' '#1.t2'
Here #1
is the first glob part i.e. the *
in *.t1
.
Or in pure bash stuff, a simple way would be:
for f in *.t1; do
mv "$f" "${f%.t1}.t2"
done
(i.e.: for
can list files without the help of an external command such as ls
or find
)
HTH

- 2,866
- 1
- 17
- 29

- 24,463
- 4
- 54
- 90
-
8I assume the OP's use of "recursively" refers to renaming files in subdirectories of the directory as well. – chepner Feb 27 '14 at 00:59
-
1
My lazy copy-pasting of one of these solutions didn't work, but I already had fd-find
installed, so I used that:
fd --extension t1 --exec mv {} {.}.t2
From fd
's manpage, when executing a command (using --exec
):
The following placeholders are substituted by a
path derived from the current search result:
{} path
{/} basename
{//} parent directory
{.} path without file extension
{/.} basename without file extension

- 12,506
- 6
- 36
- 37