bash: обрезать имена файлов, сохраняя их уникальными

Я использую цикл for, подобный этому, чтобы урезать все имена файлов в папке до 16 символов:

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

Проблема в том, что всякий раз, когда два имени файла имеют одинаковые первые 16 символов, более поздний будет перезаписывать предыдущий (в OS X mv ведет себя таким образом).

Как проверить, существует ли уже файл с именем «Shortname», и если да, то заменить последний символ «Shortname» цифрой. Затем снова проверьте, существует ли файл с таким именем, и если да, попробуйте увеличить номер. И так далее. Если он достигает числа 9 и до сих пор все имена были заняты, он должен заменить последние ДВА символа «Короткого имени» на «10» и проверить, существует ли этот файл.

Пример: допустим, у меня есть каталог со следующими файлами:

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

Обратите внимание, что первые 16 букв одинаковы для всех файлов. После запуска скрипта я бы хотел, чтобы они были переименованы в это:

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

Не имеет значения, переименован ли «MyTerriblyLongLongFourteenthFile.jpg» в «MyTerriblyLong14.jpg», это зависит от сортировки по алфавиту. Просто важно, чтобы каждый из них получил уникальное имя.

Как лучше всего это сделать?


person Martin    schedule 09.01.2011    source источник


Ответы (1)


Сначала попробуйте это на тестовых файлах. Обычный метод тестирования с использованием echo вместо mv мало что вам даст, так как не возникнет потенциальных конфликтов имен.

#!/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

Тестирование:

$ 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
person Dennis Williamson    schedule 09.01.2011