我需要重命名大约 70,000 个文件。例如:从sb_606_HBO_DPM_0089000
到sb_606_dpm_0089000
等等。
编号范围从 到0089000
。0163022
只有名称的第一部分需要更改。所有文件都在一个目录中,并按顺序编号(图像序列)。数字必须保持不变。
当我在 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 '*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