bash:截断文件名,保持它们唯一

发布于 2024-10-11 05:46:51 字数 1057 浏览 11 评论 0原文

我正在使用类似于此的 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”替换“Shortname”的最后两个字符,并检查该文件是否已存在。

示例:假设我有一个目录,其中包含以下文件:

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”并不重要,这取决于字母排序。重要的是他们每个人都有一个独特的名字。

最好的方法是什么?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

冷心人i 2024-10-18 05:46:51

首先在测试文件上尝试此操作。使用 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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文