使用 bash 重命名大量图像文件

使用 bash 重命名大量图像文件

我需要重命名大约 70,000 个文件。例如:从sb_606_HBO_DPM_0089000sb_606_dpm_0089000等等。

编号范围从 到00890000163022只有名称的第一部分需要更改。所有文件都在一个目录中,并按顺序编号(图像序列)。数字必须保持不变。

当我在 bash 中尝试这个时,它向我抱怨“参数列表太长”。

编辑:

我首先尝试使用以下命令重命名单个文件mv

mv sb_606_HBO_DPM_0089000.dpx sb_606_dpm_0089000.dpx

然后我尝试重命名一个范围(我上周在这里学习了如何移动大量文件,所以我认为相同的语法可能适用于重命名文件......)。我思考我尝试了以下操作(或类似的操作):

mv sb_606_HBO_DPM_0{089000..163023}.dpx sb_606_dpm_0{089000..163023}.dpx

答案1

一种方法是使用find-exec选项+。这会构造一个参数列表,但会将列表拆分为尽可能多的调用,以对所有文件进行操作,而不会超过最大参数列表。当所有参数都被视为相同时,这种方法很合适。 就是这样rename,但 不是mv

您可能需要安装 Perl 重命名:

sudo apt install rename

然后您可以使用,例如:

find . -maxdepth 1 -exec rename -n 's/_HBO_DPM_/_dpm_/' {} +

测试后删除-n,以实际重命名文件。

答案2

我将建议三种替代方案。每种都是简单的单行命令,但我会为更复杂的情况提供变体,主要是当要处理的文件与同一目录中的其他文件混合在一起时。

韓國

我会用mmv 命令 来自同名包

mmv '*HBO_DPM*' '#1dpm#2'

请注意,参数以字符串形式传递,因此 shell 中不会发生 glob 扩展。该命令只接收两个参数,然后在内部查找相应的文件,对文件数量没有严格限制。还请注意,上述命令假定所有与第一个 glob 匹配的文件都应重命名。当然,您可以更具体:

mmv 'sb_606_HBO_DPM_*' 'sb_606_dpm_#1'

如果同一目录中的文件超出了请求的数字范围,则最好使用此答案中给出的数字循环。但是,您也可以使用具有适当模式的一系列 mmv 调用:

mmv 'sb_606_HBO_DPM_0089*'       'sb_606_dpm_0089#1'    # 0089000-0089999
mmv 'sb_606_HBO_DPM_009*'        'sb_606_dpm_009#1'     # 0090000-0099999
mmv 'sb_606_HBO_DPM_01[0-5]*'    'sb_606_dpm_01#1#2'    # 0100000-0159999
mmv 'sb_606_HBO_DPM_016[0-2]*'   'sb_606_dpm_016#1#2'   # 0160000-0162999
mmv 'sb_606_HBO_DPM_01630[01]?'  'sb_606_dpm_01630#1#2' # 0163000-0163019
mmv 'sb_606_HBO_DPM_016302[0-2]' 'sb_606_dpm_016302#1'  # 0163020-0163022

循环数字

如果您不想安装任何东西,或者需要按数字范围选择以避免超出此范围的匹配,并且您准备等待 74,023 次命令调用,则可以使用简单的 bash 循环:

for i in {0089000..0163022}; do mv sb_606_HBO_DPM_$i sb_606_dpm_$i; done

由于序列中没有间隙,这种方法在这里尤其有效。否则,您可能想检查源文件是否确实存在。

for i in {0089000..0163022}; do
  test -e sb_606_HBO_DPM_$i && mv sb_606_HBO_DPM_$i sb_606_dpm_$i
done

请注意,与括号扩展相反,for ((i=89000; i<=163022; ++i))自几年前的某些 Bash 版本以来,括号扩展确实处理了前导零。实际上是我要求的更改,所以我很高兴看到它的用例。

进一步阅读: 括号扩展在 Bash 信息页面中,特别是关于的部分{x..y[..incr]}

循环遍历文件

另一个选择是循环遍历合适的 glob,而不是仅仅循环遍历所讨论的整数范围。如下所示:

for i in *HBO_DPM*; do mv "$i" "${i/HBO_DPM/dpm}"; done

同样,这是mv每个文件一次调用。同样,循环遍历一长串元素,但整个列表不会作为参数传递给子进程,而是由 bash 内部处理,因此限制不会给您带来问题。

进一步阅读: Shell 参数扩展在 Bash 信息页面中,记录${parameter/pattern/string}等等。

如果您想要将数字范围限制为您提供的数字范围,您可以添加检查:

for i in sb_606_HBO_DPM_+([0-9]); do
  if [[ "${i##*_*(0)}" -ge 89000 ]] && [[ "${i##*_*(0)}" -le 163022 ]]; then
    mv "$i" "${i/HBO_DPM/dpm}"
  fi
done

这里从中${i##pattern}删除最长前缀匹配。最长前缀定义为任意字符,然后是下划线,然后是零个或多个零。后者写为,它是扩展的pattern$i*(0)全局模式这取决于extglob选项正在设置。删除前导零对于将数字视为十进制而不是八进制非常重要。+([0-9])循环中的参数是另一个扩展的 glob,匹配一个或多个数字,以防万一您那里的文件以相同的数字开头但不以数字结尾。

答案3

解决该ARG_MAX限制的一种方法是使用 bash shell 的内置命令printf

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'

前任。

rename -n 's/HBO_DPM/dpm/' sb_*
bash: /usr/bin/rename: Argument list too long

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'
rename(sb_606_HBO_DPM_0089000, sb_606_dpm_0089000)
.
.
.
rename(sb_606_HBO_DPM_0163022, sb_606_dpm_0163022)

答案4

你可以编写一个小的 Python 脚本,例如:

import os
for file in os.listdir("."):
    os.rename(file, file.replace("HBO_DPM", "dpm"))

将其保存为文本文件,保存rename.py在文件所在的文件夹中,然后使用该文件夹中的终端执行:

python rename.py

相关内容