用bash重命名大量图像文件


16

我需要重命名。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

4
致审稿人:我不认为这是重复的;由于大量文件与Shell的ARG_MAX限制冲突,因此其他问题上的大多数CLI答案在这里都不起作用。由于此问题明确要求提供命令行解决方案,因此(可能相等)GUI解决方案也与其他问题不匹配。
甜点

1
我不认为这是一个骗子,因为可以有多个有关重命名文件的问题。请不要针对无法真正回答问题的通用资源关闭特定问题……
赞纳

1
@rich如果您可以显式地编辑您尝试过的命令,那么可以很清楚地看出这不是伪造。(这向我们表明您知道此方法。)干杯。
Sparhawk '18

2
富有,您的问题不是骗人的,因为这是一个特定的问题。不用担心 更重要的是,在一个问题收到许多建议的答案之后,对其进行编辑可能不是一个好主意,因为您的编辑可能会使现有答案的有效性降低。现在,我觉得我的答案应该解释为什么mv {1..2} {3..4}不起作用,这是与ARG_MAX... 完全不同的问题。其他回答的人可能也会有同感!因此,从我的角度来看,我希望您回滚您的上一次编辑,并且,如果您愿意,问一个关于mv范围内ing的全新问题
Zanna

1
从问题的第一个版本开始,OP的@Sparhawk写得很清楚,问题是argument list too long错误。无需进一步说明,这显然不是一个重复,因为我们需要一种解决方法来处理ARG_MAX,建议的重复项中的答案也不能做到这一点。
特登

Answers:


25

一种方法是find-exec+选项一起使用。这将构造一个参数列表,但会将列表分为多个调用以对所有文件进行操作,而不会超出最大参数列表。当所有参数都相同时适用。确实是这种情况rename,尽管不是mv

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

sudo apt install rename

然后,您可以使用例如:

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

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


11

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

毫米波

我会用MMV命令包同名

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

请注意,参数作为字符串传递,因此在外壳程序中不会发生全局扩展。该命令恰好接收两个参数,然后在内部查找对应的文件,而文件数量没有严格的限制。还要注意,上面的命令假定与第一个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}删除最长的前缀匹配项。最长的前缀定义为任何东西,然后是下划线,然后是零个或多个零。后者写为扩展pattern$i*(0) glob模式,具体取决于所设置的extglob选项。删除前导零对于将数字视为10而不是8是很重要+([0-9])的。loop参数是另一个扩展的glob,匹配一个或多个数字,以防万一您那里有以相同的开头但不以。结尾的文件数。


谢谢!这个工作就像一个梦:对于{0089000..0163022}中的我来说;做MV sb_606_HBO_DPM_ $ i sb_606_dpm_ $ i; 完成-我必须添加文件名扩展名才能使其正常工作,但它确实达到了我想要的目的,我什至理解了语法。谢谢@MvG-
丰富的

@rich:很高兴我能为您提供帮助-希望您也能为将来的访客提供帮助。不要忘记接受最有用的答案。如果出现更好的情况,将来您始终可以更改该复选标记。
MvG

10

解决问题的一种方法 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)

7
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使用替换HBO_DPM为重命名找到的文件dmp 一一-exec ... \;

替换echomv以执行重命名。


6

您可以编写一些python脚本,例如:

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

将其另存为文本文件rename.py,与文件所在的文件夹相同,然后将终端放在该文件夹中:

python rename.py

6

您可以使用以下命令逐个文件处理(可能需要一些时间)

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

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


我已经删除了您对Zanna答案的评论,请编辑Zanna的答案或发表评论。
fosslinux

@ubashu并不是对我的回答的评论-它是指-n我用于测试的标志,并暗示也可以使用它rename.ul
Zanna

3

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

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

有许多用于完成这项工作的工具,请选择最适合您的工具。这个很简单,很容易更改以适合此目的或其他目的。


当然,在这种特定情况下不太相关,但是如果任何文件名包含换行符,则此操作将失败。我之所以这样说是因为,大多数(全部?)其他答案都是可靠的,可以处理任意文件名,或者适用于OP的文件命名方案。
特登

...换行符,空格,通配符...可以通过$i在命令替换中加引号来避免某些操作,但没有简单的方法来处理文件名中的尾随换行符。
muru

3

由于我们提供了选择,因此这里有一种Perl方法。cd进入目标目录并运行:

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

说明

  • perl -e:运行给出的脚本-e
  • foreach(glob){}{ }对glob的每个结果运行任何内容。
  • glob("sb_*"):返回当前目录中名称与shell glob匹配的所有文件和目录的列表sb*
  • rename $_, s/_HBO_DPM_/_dpm_/r:perl魔术。$_是一个特殊变量,用于保存我们要遍历的每个元素(位于中foreach)。因此,这里将找到每个文件。s/_HBO_DPM_/_dpm_/替换第一次出现_HBO_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;
}

1

根据您所设想的重命名类型,将vidir与多行编辑配合使用可能会令人满意。
在您的特殊情况下,您可以在文本编辑器中选择所有行,并通过几次按键删除文件名的_“ HBO”部分。


是的,vi很高兴找到并替换。
杰森

2
您能否扩大答案,并举例说明如何实现OP的目标vidir
甜点
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.