如何在不发生冲突的情况下移动文件名编号?

l0b*_*0b0 6 bash sort rename

考虑这个文件列表:

$ touch index-{1,2,3,4,5,6,7,8,9}.txt
Run Code Online (Sandbox Code Playgroud)

如果我想将它们向下移动以便它们从零开始,则相对容易

$ rename --verbose 's/^index-([1-9])\.txt$/$1/; $_="index-" . ($_ - 1) . ".txt"' index-*.txt
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为man bash指定 globs 按字母顺序排序,所以它会重命名index-1.txtindex-0.txt 之前重命名index-2.txtindex-1.txt

如果你想向上移动,或者如果数字有不同的长度,这会崩溃:

$ touch index-{10,11}.txt
$ rename --verbose 's/^index-([0-9]+)\.txt$/$1/; $_="index-" . ($_ + 1) . ".txt"' index-*.txt
index-10.txt not renamed: index-11.txt already exists
index-11.txt renamed as index-12.txt
index-1.txt not renamed: index-2.txt already exists
Run Code Online (Sandbox Code Playgroud)

可能的长期修复:

  • 一个rename选项来尝试重新排序操作,直到没有冲突。
  • 首先rename将文件移动到临时目录的选项,然后使用新名称将它们移回。
  • rename分两步重命名文件的选项,在第一步中使用不会与原始名称或新名称冲突的唯一名称。
  • 一种进行自然排序 + Bash glob反转的方法。

有没有一种简单的方法可以解决这个问题?

Sté*_*las 2

@l0b0 的解决方案重写以获得更好的鲁棒性:

printf '%s\0' index-*.txt |
  sort --zero-terminated --field-separator - --key 2rn |
  xargs -0r rename --verbose '
    s/^index-([0-9]+)\.txt$/$1/;
    $_="index-" . ($_ + 1) . ".txt"'
Run Code Online (Sandbox Code Playgroud)

请随意包含在您的解决方案中,之后我将删除我的答案。

请注意,@l0bo 的解决方案是 GNU 特定的,而不是 Unix(GNU's Not Unix)。