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

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

考虑这个文件列表:

$ touch index-{1,2,3,4,5,6,7,8,9}.txt

如果我想改变它们向下所以他们从零开始,就是相对容易:

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

这是有效的,因为man bash指定 glob 按字母顺序排序,因此它将重命名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

可能的长期修复:

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

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

答案1

Unix 上有一个鲜为人知的标准命令,可以帮助我们找到一个通用的解决方案来确定一系列重命名必须按什么顺序进行:tsort

假设我们有一个要在名为的文件中完成的重命名列表renames.txt(假设为了演示,它们的名称不包含空格):

d a
e f
b e
a c

因为d是要改名a,那就意味着a之前必须改名d。因此,我们有一个部分排序顺序,这与文件应重命名的顺序相反。

tsort是从部分排序顺序列表推断完整排序顺序的工具。如果存在循环,它将返回错误,这将帮助我们检测没有解决方案的情况。如果我们应用tsort该输入,它会给我们:

b
d
e
a
f
c

其中说b应该在dafter 之后重命名e。我们可以用GNU tac(有些系统也有tail -r)来颠倒该顺序:

c
f
a
e
d
b

并将其与我们的重命名列表一起加入:

tsort renames.txt | tac | awk '
  NR==FNR {
    ren[$1] = $2
    next
  }
  $1 in ren {
    print "mv -i --", $1, ren[$1]
  }' renames.txt -

这给了我们:

mv -i -- a c
mv -i -- e f
mv -i -- d a
mv -i -- b e

然后我们可以通过管道来sh执行。

但请注意,上面的代码并不健壮,因为我们没有检查tsort上面的退出状态来检测循环,并且文件名不得包含任何特殊的 shell 字符。

鲁棒性留给读者作为练习;-)

答案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"'

请随意添加您的解决方案,然后我会删除我的答案。

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

答案3

这适用于上面的具体情况:

rename --verbose 's/^index-([0-9]+)\.txt$/$1/; $_="index-" . ($_ + 1) . ".txt"' $(printf '%s\n' index-*.txt | sort --field-separator - --key 2n | tac)

但:

  1. 它不处理空白在文件名中,以及
  2. 对于任意文件名来说,情况会复杂得多。

答案4

对于上下文,我使用 LapseIt Pro 应用程序进行延时拍摄,并且由于某种原因它停止了,然后我重新启动了延时拍摄,但它给了它一个不同的项目名称,例如:-

Proj10_img0000000x

我需要将所有 3000 个左右的文件重命名为之前的项目 Proj9_img0000000x 并修改末尾的数字,以便该项目具有连续的名称+数字提要,这样我就可以将所有图像渲染为一个项目视频

Proj10_img00000001 --> Proj10_img00003249 需要更改为 Proj9_img00004325 --> Proj9_img00007574

所以总而言之,最终结果在项目文件夹 Proj9_img00000001 --> Proj9_img00007574 中看起来像这样

如果使用 Mac OSX(我使用的是 Mojave),你可以

  1. 在查找器中突出显示所有要修改的文件
  2. 右键单击突出显示的文件部分
  3. 选择重命名“X 数量”项目
  4. 将打开“重命名查找器项目”弹出窗口
  5. 此弹出窗口允许您轻松重命名和重新编号以及一些其他方便的选项。

我仍在查看命令行 grep/sed/awk/cut & find 命令,但目前上述方法有效(我需要尽快完成视频以便发布)

相关内容