Renaming hundreds of files at once for proper sorting

Ubuntu comes with a script called rename. It's just a little Perl script that features a number of powerful bulk-renaming features but the best (in this case) is the ability for it to run Perl during the replacement. The result is a truly compact solution:

rename 's/\d+/sprintf("%05d", $&)/e' *.jpg

This is similar to the other printf-style answers here but it's all handled for us. The code above is for a 5-digit number (including a variable number of leading zeros).

It will search and replace the first number-string it finds with a zero-padded version and leave the rest of the filename alone. This means you don't have to worry too much about carrying any extension or prefix over.

Note: this is not completely portable. Many distributions use rename.ul from the util-linux package as their default rename binary. This is a significantly stunted alternative (see man rename.ul) which won't understand the above. If you'd like this on a platform that isn't using Perl's rename, find out how to install that first.


And here's a test harness:

$ touch {1..19}.jpg

$ ls
10.jpg  12.jpg  14.jpg  16.jpg  18.jpg  1.jpg  3.jpg  5.jpg  7.jpg  9.jpg
11.jpg  13.jpg  15.jpg  17.jpg  19.jpg  2.jpg  4.jpg  6.jpg  8.jpg

$ rename 's/\d+/sprintf("%05d", $&)/e' *.jpg

$ ls
00001.jpg  00005.jpg  00009.jpg  00013.jpg  00017.jpg
00002.jpg  00006.jpg  00010.jpg  00014.jpg  00018.jpg
00003.jpg  00007.jpg  00011.jpg  00015.jpg  00019.jpg
00004.jpg  00008.jpg  00012.jpg  00016.jpg

And an example prefixes (we aren't doing anything different):

$ touch track_{9..11}.mp3 && ls
track_10.mp3  track_11.mp3  track_9.mp3

$ rename 's/\d+/sprintf("%02d", $&)/e' *.mp3 && ls
track_09.mp3  track_10.mp3  track_11.mp3

for f in *.jpg ; do if [[ $f =~ [0-9]+\. ]] ; then  mv $f `printf "%.5d" "${f%.*}"`.jpg  ; fi ; done

Edit

Explanation:

  • if [[ $f =~ [0-9]+\. ]] makes sure that only files whose names are numbers (followed by a dot) are being renamed.
  • printf "%.5d" NUMBER adds the leading zeroes
  • "${f%.*}" cuts the extension (.jpg) and leaves just the number
  • .jpgafter the second backtick adds the file extension again.

Note that this will work only on file names that are numbers. Left-padding leading zeroes to non-numbered files would require different format.

If you want to experiment try this command:

for f in *.jpg ; do if [[ $f =~ [0-9]+\. ]] ; then echo mv $f `printf "%.5d" "${f%.*}"`.jpg  ; fi ; done

Edit 2

Made the command safer by making sure that only file names that are numbers are being renamed. Note that any pre-existing files named like 00001.jpg will be overwritten.


Below a python script.

The script adds leading zeros up to the defined number of digits. If the name is larger than that, the file(name) is untouched.

Combining different extensions in one rename action might add some convenience. To add extension(s), simply add them to the tuple, for example extensions = (".jpg", ".jpeg", ".tiff").

Copy the text into an empty file, save it as rename.py, enter the correct path to the files directory (sourcedir), the number of digits you'd like the new names to have (number_ofdigits) and the file extension(s) to rename (extensions)

Run it by the command:

python3 /path/to/script/rename.py

The script:

#!/usr/bin/python3

import shutil
import os

sourcedir = "/path/to/files"; number_ofdigits = 5; extensions = (".jpg", ".jpeg")

files = os.listdir(sourcedir)
for item in files:
    if item.endswith(extensions):
        name = item.split("."); zeros = number_ofdigits-len(name[0])
        newname = str(zeros*"0")+name[0]+"."+name[1]
        shutil.move(sourcedir+"/"+item, sourcedir+"/"+newname)

edit:

Below a slightly improved version. It automatically determines the longest name in the directory, and adds leading zeros up to the length of the longest name.

example:

1.jpg
12.jpg
123.jpg

becomes:

001.jpg
012.jpg
123.jpg

No need to set the number of digits.

#!/usr/bin/python3

import shutil
import os

sourcedir = "/path/to/files"; extensions = (".jpg", ".jpeg")
files = [(f, f[f.rfind("."):], f[:f.rfind(".")]) for f in os.listdir(sourcedir)if f.endswith(extensions)]
maxlen = len(max([f[2] for f in files], key = len))

for item in files:
    zeros = maxlen-len(item[2])
    shutil.move(sourcedir+"/"+item[0], sourcedir+"/"+str(zeros*"0")+item[0])