55

I have many files with .txt extension. How to remove .txt extension for multiple files in linux?

I found that

rename .old .new *.old

substitutes .old extension to the .new

Also I want to do this for files in sub-folders.

mechanical_meat
  • 163,903
  • 24
  • 228
  • 223
rp101
  • 1,239
  • 2
  • 10
  • 10

8 Answers8

87

rename is slightly dangerous, since according to its manual page:

rename will rename the specified files by replacing the first occurrence of...

It will happily do the wrong thing with filenames like c.txt.parser.y.

Here's a solution using find and bash:

find -type f -name '*.txt' | while read f; do mv "$f" "${f%.txt}"; done

Keep in mind that this will break if a filename contains a newline (rare, but not impossible).

If you have GNU find, this is a more solid solution:

find -type f -name '*.txt' -print0 | while read -d $'\0' f; do mv "$f" "${f%.txt}"; done
robsch
  • 9,358
  • 9
  • 63
  • 104
thkala
  • 84,049
  • 23
  • 157
  • 201
  • 1
    That's true regarding the `util-linux-ng` version of `rename`, but the Perl script version can do `rename 's/.txt$//' *.txt` – Dennis Williamson Dec 22 '10 at 15:34
  • 1
    you are missing current "." as for the location where you want to run in. maybe this is my osx variant of find, or my path is not set correctly – bgs Aug 09 '13 at 17:29
  • 3
    @bgs: most (all?) Linux distributions use GNU find, which defaults to . when no argument has been specified – thkala Aug 09 '13 at 22:02
  • 4
    yes, don't forget the directory specifier if you are on Mac. e.g. `find . -type f -name '*.txt' | while read f; do mv "$f" "${f%.txt}"; done` – lolski Oct 25 '13 at 08:27
  • Why is the second one more solid than the first one? –  Oct 05 '18 at 07:12
25

I use this:

find ./ -name "*.old" -exec sh -c 'mv $0 `basename "$0" .old`.new' '{}' \;
Chris Cowan
  • 251
  • 3
  • 2
  • thank you, I didn't know this syntax, and it works also on MacOSX (which lacks native `rename` ). – Utopik Jul 15 '13 at 08:47
  • You should have added the dirname in case target is in a subfolder : find ./ -name "*.old" -exec sh -c 'mv $0 `dirname "$0"`/`basename "$0" .old`.new' '{}' \; – Séverin May 29 '17 at 08:10
17

The Perl version of rename can remove an extension as follows:

rename 's/\.txt$//' *.txt

This could be combined with find in order to also do sub-folders.

Andrew S
  • 426
  • 5
  • 6
10

You can explicitly pass in an empty string as an argument.

rename .old '' *.old

And with subfolders, find . -type d -exec rename .old '' {}/*.old \;. {} is the substitute for the entry found with find, and \; terminates the arglist for the command given after -exec.

robert
  • 33,242
  • 8
  • 53
  • 74
  • I don't know what version of `rename` you used there, but it doesn't work here on the Debian version, which is a perl script that uses regex for the arguments. This works: `$ rename 's/\.old//' *.old` – paradroid Jun 22 '21 at 16:13
3

In case it helps, here's how I do it with zsh:

for f in ./**/*.old; do
    mv "${f}" "${f%.old}"
done

The ${x%pattern} construct in zsh removes the shortest occurence of pattern at the end of $x. Here it is abstracted as a function:

function chgext () {
    local srcext=".old"
    local dstext=""
    local dir="."

    [[ "$#" -ge 1 ]] && srcext="$1"
    [[ "$#" -gt 2 ]] && dstext="$2" dir="$3" || dir="${2:-.}"

    local bname=''
    for f in "${dir}"/**/*"${srcext}"; do
        bname="${f%${srcext}}"
        echo "${bname}{${srcext} → ${dstext}}"
        mv "${f}" "${bname}${dstext}"
    done
}

Usage:

chgext
chgext src
chgext src dir
chgext src dst dir

Where `src` is the extension to find (default: ".old")
      `dst` is the extension to replace with (default: "")
      `dir` is the directory to act on (default: ".")
svvac
  • 5,814
  • 3
  • 17
  • 22
2

In Fish, you can do

for file in *.old
      touch (basename "$file" .old).new
end
Bruno Alano
  • 643
  • 1
  • 11
  • 21
1

For subfolders:

for i in `find myfolder -type d`; do
  rename .old .new $i/*.old
done
Konrad Garus
  • 53,145
  • 43
  • 157
  • 230
1

execute in bash linux

for i in *;do mv ${i} ${i/%.pdf/} ;done
Dmitry
  • 6,716
  • 14
  • 37
  • 39