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.