Bash – How to Rename Files by Padding Numbers in Filenames

bashrenameshell

I have a directory on a Deepin (Debian-based) system which contains video files, something like this:

SL Benfica - Match 1 vs FC Porto.mp4
SL Benfica - Match 2 vs FC Porto.mp4
...
SL Benfica - Match 20 vs FC Porto.mp4
...
SL Benfica - Match 100 vs FC Porto.mp4
SL Benfica - Match 101 vs FC Porto.mp4

I would like to add zeros before the numbers in the middle so that they are sorted as in

SL Benfica - Match 001 vs FC Porto.mp4
SL Benfica - Match 002 vs FC Porto.mp4
...
SL Benfica - Match 020 vs FC Porto.mp4
...
SL Benfica - Match 100 vs FC Porto.mp4
SL Benfica - Match 101 vs FC Porto.mp4

I'm looking for a general command that does not depend on patterns. Just to search numbers like 5 and 20, and change them to 005 and 020, anywhere in the filename, even in the end or beginning of it.

Best Answer

With GNU sed, you can do (even with no extension):

sed -E \
-e 'h;s/(.*)(\.[^.]*$)/\2/;ta;z;:a;x;s//\1/' \
-e 's/([0-9]{1,})/\n000\1\n/g' \
-e 's/\n[0-9]*([0-9]{3})\n/\1/g' \
-e 'G;s/\n//g' file

No loops only regexes.

The first regex will match

  • 'h;s/(.*)(\.[^.]*$)/\2/;ta;z;:a;x;s//\1/' place the extension in the hold space (if there was one). Leave the rest in the pattern space.
  • 's/([0-9]{1,})/\n000\1\n/g' Place each stream of numbers in separate lines prepending them with 3 zeros.

After each number to modify has been extended to have leading zeros and being in their own line, do:

  • 's/\n[0-9]*([0-9]{3})\n/\1/g' Extract the last 3 digits from each line of digits.
  • 'G;s/\n//' re-attach the extension captured at the start.

If the source list is:

➤ cat file
1- 23.m2
ATestFile.mp4
SomeFile.m4a
AFileWithNumbers23inside.mp4
File 1 with 12 some 100 numbers 3.mp4
SL Benfica - Match 1 vs FC Porto.mp4
SL Benfica - Match 2 vs FC Porto.mp4
SL Benfica - Match 20 vs FC Porto.mp4
SL Benfica - Match 101 vs FC Porto.mp4

The result will be:

001- 023.m2
ATestFile.mp4
SomeFile.m4a
AFileWithNumbers023inside.mp4
File 001 with 012 some 100 numbers 003.mp4
SL Benfica - Match 001 vs FC Porto.mp4
SL Benfica - Match 002 vs FC Porto.mp4
SL Benfica - Match 020 vs FC Porto.mp4
SL Benfica - Match 101 vs FC Porto.mp4

So, to rename files (filenames should not contain newlines and the extension should follow a dot):

#!/bin/bash

for old in *; do
    new=$(  printf '%s\n' "$old" | 
                sed -E \
                    -e 'h;s/(.*)(\.[^.]*$)/\2/;ta;z;:a;x;s//\1/' \
                    -e 's/([0-9]{1,})/\n000\1\n/g' \
                    -e 's/\n[0-9]*([0-9]{3})\n/\1/g' \
                    -e 'G;s/\n//'
         );
    if [[ ! -f $new ]]; then
        echo \
            mv "$old" "$new";
    fi
done

Remove the echo after you are satisfied with the script to actually change the files.

Related Question