I have a directory with a bunch of files with names like this:

  • generic name- dd -01 _.txt
  • generic name- dd -02 _.txt
  • generic name- dd -03 _.txt
  • generic name- dd -04 _.txt

And I want to rename all of them, keeping only the numbers and the extension like this:

  • 01.txt
  • 02.txt
  • 03.txt
  • 04.txt

How can I do this using a shell script?


Perl rename

I would use (perl-)rename. Distros ship two types of rename, so you can check if you have the perl version of rename installed with

$ rename --version
perl-rename 1.9

Run it with -n to test the command as a "dry run".

$ rename 's/[^0-9]*([0-9]*).*/\1.txt/' * -n
generic name- dd -01 _.txt -> 01.txt
generic name- dd -02 _.txt -> 02.txt
generic name- dd -03 _.txt -> 03.txt
generic name- dd -04 _.txt -> 04.txt

When you are happy run the command itself.

$ rename 's/[^0-9]*([0-9]*).*/\1.txt/' *


  • rename 's/foo/bar/' *: rename all files (*), replacing the regex foo with bar.
  • [^0-9]*([0-9]*).*: match all non-numerical characters [^0-9]*, followed by numerical characters, which are enclosed in a "capturing group" ([0-9]*), followed by the rest of the characters .*. This will match the whole filename.
  • \1.txt: replace the filename with the captured group \1, appended with .txt.

The other rename

If you have the simpler rename installed, you will see something like this instead.

$ rename --version
rename from util-linux 2.33.1

In this case you can use the following two commands:

$ rename 'generic name- dd -' '' *
$ rename ' _' '' *

But I strongly recommend learning perl-rename instead.


We can do with below one liner. Tested it. But it needs to be executed from the current folder where file is present.

for file in *generic*.txt; do newname=$(echo $file | awk -F"-" '{print $3}' | sed 's/ _//g'); mv "$file" $newname;done

if you want to run from different folder you can try with below script


for file in $SRCFLDR/*generic*.txt;do
        newname=$(echo $file | awk -F"-" '{print $3}' | sed 's/ _//g')
        mv "$file" $newname
        echo $file has been renamed to $newname

This simple script should do it:

for f in *[0-9]*.*; do
  mv -- "$f" "${base//[^0-9]}.${f##*.}"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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