6

I'm using a for-loop similar to this one to truncate all filenames in a folder to 16 characters:

for i in *; do
    Shortname=${i:0:16}     # Let's assume I don't need the extension
    mv "$i" "$Shortname"
done

The problem is: Whenever two filenames have the same first 16 characters, the later one will overwrite the previous one (on OS X mv behaves that way).

How can I check if a file with the name "Shortname" already exists, and if so, replace the last character of "Shortname" with a number. Then check again if a file with that name exists, and if so, try a higher number. And so on. If it arrives at number 9 and so far all names have been taken, it should replace the last TWO characters of the "Shortname" with "10" and check if that file already exists.

Example: Let's say I have a directory with the following files in it:

MyTerriblyLongLongFirstFile.jpg
MyTerriblyLongLongSecondFile.jpg
MyTerriblyLongLongThirdFile.jpg
...
MyTerriblyLongLongFourteenthFile.jpg
...
MyTerriblyLongLongOneHundredSixtySeventhFile.jpg
...
MyTerriblyLongLongFiveMillionthFile.jpg

Note that the first 16 letters are the same for all files. After running the script, I would like them to be renamed to this:

MyTerriblyLongL1.jpg
MyTerriblyLongL2.jpg
MyTerriblyLongL3.jpg
...
MyTerriblyLong14.jpg
...
MyTerriblyLon167.jpg
...
MyTerribl5000000.jpg

It doesn't matter if "MyTerriblyLongLongFourteenthFile.jpg" is renamed to "MyTerriblyLong14.jpg", that depends on alphabetical sorting. It's just important that they each get a unique name.

What is the best way to do this?

Martin
  • 63
  • 1
  • 3

1 Answers1

5

Try this on test files first. The usual method of testing by using echo instead of mv won't tell you much since the potential name collisions wouldn't be created.

#!/bin/bash
num=1
length=16
for file in M*.jpg
do
    newname=$file
    until [[ ! -f $newname ]]
    do
        (( sublen = length - ${#num} ))
        printf -v newname '%.*s%d' "$sublen" "$file" "$num"
        (( num++ ))
    done
    mv "$file" "$newname"
done

Testing:

$ touch MyTerriblyLongLongFilenames{a..k}.jpg
$ touch MyTerriblyLongL3
$ ls M*
MyTerriblyLongL3                  MyTerriblyLongLongFilenamesf.jpg
MyTerriblyLongLongFilenamesa.jpg  MyTerriblyLongLongFilenamesg.jpg
MyTerriblyLongLongFilenamesb.jpg  MyTerriblyLongLongFilenamesh.jpg
MyTerriblyLongLongFilenamesc.jpg  MyTerriblyLongLongFilenamesi.jpg
MyTerriblyLongLongFilenamesd.jpg  MyTerriblyLongLongFilenamesj.jpg
MyTerriblyLongLongFilenamese.jpg  MyTerriblyLongLongFilenamesk.jpg
$ ./nocollide
$ ls M*
MyTerriblyLong10  MyTerriblyLongL1  MyTerriblyLongL4  MyTerriblyLongL7
MyTerriblyLong11  MyTerriblyLongL2  MyTerriblyLongL5  MyTerriblyLongL8
MyTerriblyLong12  MyTerriblyLongL3  MyTerriblyLongL6  MyTerriblyLongL9
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439