用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 

一种方法是使用find with -exec+选项。 这构造了一个参数列表,但是在不超过最大参数列表的情况下,将列表分解为对所有文件进行操作所需的调用次数。 当所有参数都被视为相同时,它是合适的。 这是rename的情况,但不是mv

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

 sudo apt install rename 

然后你可以使用,例如:

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

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

我打算提出三种选择。 每个都是一个简单的单行命令,但我会为更复杂的情况提供变体,主要是在要处理的文件与同一个目录中的其他文件混合的情况下。

MMV

我将使用同名 包中的 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信息页面中的Brace Expansion ,特别是关于{x..y[..incr]}

循环文件

另一种选择是循环一个合适的glob,而不是仅仅循环遍及所讨论的整数范围。 像这样的东西:

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

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

进一步阅读: Bash信息页面中的Shell参数扩展 ,记录${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}$i删除了最长的前缀匹配pattern 。 最长的前缀被定义为任何东西,然后是下划线,然后是零或更多的零。 后者写为*(0) ,它是一个扩展的glob模式 ,取决于所设置的extglob选项 。 删除前导零对于将数字视为基数10而不是基数8很重要。循环参数中的+([0-9])是另一个扩展的glob,匹配一个或多个数字,以防万一你有文件启动相同但不以数字结尾。

解决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) 
 find . -type f -exec bash -c 'echo $1 ${1/HBO_DPM/dpm}' _ {} \; ./sb_606_HBO_DPM_0089000 ./sb_606_dpm_0089000 

在当前目录中find . 对于所有文件-type f并重命名找到$1的文件,用dmp 逐个替换HBO_DPM -exec ... \;

mv替换echo以执行重命名。

你可以写一个小的python脚本,如:

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

将其作为文件文件保存为文件rename.py的文件夹中的rename.py ,然后使用该文件夹中的终端:

 python rename.py 

您可以逐个文件地进行(可能需要一些时间)

 sudo apt install util-linux # if you don't have it already for i in *; do rename.ul HBO_DPM dpm "$i"; done 

与其他答案中使用的Perl rename一样, rename.ul也有一个选项-n--no-act用于测试。

我看到没有人邀请我最好的朋友参加聚会:)。 以下for循环将实现您的目标:

 for i in sb_606_HBO_DPM*; do mv "$i" "$(echo $i | sed 's/HBO_DPM/dpm/')"; done 

这样的工作有很多工具,选择一个最容易理解的工具。 这个很简单,很容易改变,以适应这个或其他目的……

由于我们提供选项,这是一个Perl方法。 cd进入目标目录并运行:

 perl -e 'foreach(glob("sb_*")){rename $_, s/_HBO_DPM_/_dpm_/r}' 

说明

  • perl -e :运行-e给出的脚本。
  • foreach(glob){} :在glob的每个结果上运行foreach(glob){}任何内容。
  • glob("sb_*") :返回当前目录中名称与shell glob sb*匹配的所有文件和目录的列表。
  • rename $_, s/_HBO_DPM_/_dpm_/r :perl magic。 $_是一个特殊变量,它包含我们迭代的每个元素(在foreach )。 所以在这里,将找到每个文件。 s/_HBO_DPM_/_dpm_/s/_HBO_DPM_/_dpm_/替换第一次出现的_dpm_ 。 它默认运行在$_ ,因此它将在每个文件名上运行。 /r表示“将此替换应用于目标字符串的副本(文件名)并返回修改后的字符串。 rename完成您所期望的:它重命名文件。所以整个事情将重命名当前文件名( $_ )自身_HBO_DPM_替换为_dpm_

您可以编写与扩展(更易读的脚本)相同的内容:

 #! /usr/bin/env perl use strict; use warnings; foreach my $fileName (glob("sb_*")){ ## Copy the name to a new variable my $newName = $fileName; ## change the copy. $newName is now the changed version $newName =~ s/_HBO_DPM_/_dpm_/; ## rename rename $fileName, $newName; } 

根据您想象的重命名类型,使用具有多行编辑的vidir可能会令人满意。
在您的特定情况下,您可以选择文本编辑器中的所有行,并在几个键击中删除文件名的_“ HBO”部分。