There are 32 *.dds files in a folder:

pic-0.dds, pic-1.dds, pic-2.dds,...,pic-31.dds

I'd like to rename them to be:

015040.dds, 015041.dds, 015042.dds,...,0150471.dds.

I'm trying to use the following script:

for i in *.dds;
do let j+=1;
mv $i 0150$j.dds;

I obtain that pic-0.dds is renamed to 015040.dds, but then it goes wrong, namely pic-1.dds isn't renamed to 015041.dds and so on.

I'd like to have an universal script for other renaming, for example:

pic-40.dds, pic-41.dds, pic-42.dds,...,pic-53.dds


0191.dds, 0192.dds, 0193.dds,...,0204.dds

2 Answers 2


The problem is pic-*.dds sorts the files asciibetically, not by the number after the dash.

One possible way to fix that is to insert a 0 after the dash to the files named pic-{0..9}.dds. Then listing the files asciibetically will be the same as sorting them by the number and everything will work.

You can do that by using a loop similar to the one you already used:

for f in pic-?.dds ; do
    mv "$f" pic-0"${f#pic-}"

The ${f#pic-} is a parameter substitution, the # sign tells the shell to remove the string pic- from the beginning of the variable $f's value.


There's no need for a shell for loop, you can use perl rename for this.

The perl rename utility is also known as file-rename, perl-rename, or prename, depending on your distribution. It is not to be confused with the rename utility from util-linux which has completely different and incompatible capabilities and command-line options. Perl rename allows you to use any arbitrarily complex perl code to rename files, but is most often used to do simple sed-like s/search/replace/ operations on filenames.

For example:

$ rename -n 's/pic-(\d+)\.dds$/sprintf("015%03i.dds",$1+40)/e' *.dds
rename(pic-0.dds, 015040.dds)
rename(pic-10.dds, 015050.dds)
rename(pic-11.dds, 015051.dds)
rename(pic-12.dds, 015052.dds)
rename(pic-13.dds, 015053.dds)
rename(pic-14.dds, 015054.dds)
rename(pic-15.dds, 015055.dds)
rename(pic-16.dds, 015056.dds)
rename(pic-17.dds, 015057.dds)
rename(pic-18.dds, 015058.dds)
rename(pic-19.dds, 015059.dds)
rename(pic-1.dds, 015041.dds)
rename(pic-20.dds, 015060.dds)
rename(pic-21.dds, 015061.dds)
rename(pic-22.dds, 015062.dds)
rename(pic-23.dds, 015063.dds)
rename(pic-24.dds, 015064.dds)
rename(pic-25.dds, 015065.dds)
rename(pic-26.dds, 015066.dds)
rename(pic-27.dds, 015067.dds)
rename(pic-28.dds, 015068.dds)
rename(pic-29.dds, 015069.dds)
rename(pic-2.dds, 015042.dds)
rename(pic-30.dds, 015070.dds)
rename(pic-31.dds, 015071.dds)
rename(pic-3.dds, 015043.dds)
rename(pic-4.dds, 015044.dds)
rename(pic-5.dds, 015045.dds)
rename(pic-6.dds, 015046.dds)
rename(pic-7.dds, 015047.dds)
rename(pic-8.dds, 015048.dds)
rename(pic-9.dds, 015049.dds)

This captures the digits in the pic-*.dds filenames and adds them to 40 to construct the final filename. The s/search/replace/ substitution operation uses perl's /e modifier to cause it to execute the right-hand-side replacement as perl code. The sprintf function is used to make sure that the calculations are formatted as 3-digit wide zero-padded strings (%03i).

  • this will work regardless of the order it sees the filenames - it doesn't use an incrementing counter, it extracts the existing number from the pic-(\d+).dds$ filenames and adds 40.
  • filenames that don't match that pattern are NOT renamed
  • perl rename will NOT overwrite existing filenames (unless you force it to by using the -f option).

Note that the -n option in the example above makes it a dry run, so it will only show what it would do without actually renaming any files. Remove the -n, or replace it with -v for verbose output, when you've confirmed it does what you want.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .