![如何将文件从一种递增模式重命名为另一种递增模式?](https://linux22.com/image/1173761/%E5%A6%82%E4%BD%95%E5%B0%86%E6%96%87%E4%BB%B6%E4%BB%8E%E4%B8%80%E7%A7%8D%E9%80%92%E5%A2%9E%E6%A8%A1%E5%BC%8F%E9%87%8D%E5%91%BD%E5%90%8D%E4%B8%BA%E5%8F%A6%E4%B8%80%E7%A7%8D%E9%80%92%E5%A2%9E%E6%A8%A1%E5%BC%8F%EF%BC%9F.png)
我有一组文件,其名称如下:
048. banana.mkv
049. apple.mkv
050. mango.mkv
051. pear.mkv
052. grape.mkv
我需要按照以下命名约定重命名这些文件
01. banana.mkv
02. apple.mkv
03. mango.mkv
04. pear.mkv
05. grape.mkv
问题是我需要重命名数百个文件,并且需要从多个组开始01
并递增。通常我会rename
批量重命名文件。在这种情况下,我一直使用qvm
更改每个文件名。肯定有更简单的方法。
答案1
使用基于 perl 的rename
实用程序:
$ rename -n 's/^(\d+)/sprintf "%02d", $1-47/e' *.mkv
rename(048. banana.mkv, 01. banana.mkv)
rename(049. apple.mkv, 02. apple.mkv)
rename(050. mango.mkv, 03. mango.mkv)
rename(051. pear.mkv, 04. pear.mkv)
rename(052. grape.mkv, 05. grape.mkv)
这里的技巧是替换e
上的修饰符s/pattern/replacement/
- 它让你埃将替换字符串作为 perl 表达式进行评估 - 包括$1-47
通过 进行算术和填充格式sprintf
。-n
当您确信它正在做正确的事情时,请删除。
如果你只有一个 shell,那么:
$ for f in *.mkv; do
n="${f%%.*}"
printf -v newf '%02d.%s' "$((10#$n-47))" "${f#*.}"
echo mv -- "$f" "$newf"
done
mv -- 048. banana.mkv 01. banana.mkv
mv -- 049. apple.mkv 02. apple.mkv
mv -- 050. mango.mkv 03. mango.mkv
mv -- 051. pear.mkv 04. pear.mkv
mv -- 052. grape.mkv 05. grape.mkv
echo
一旦您确认它做了正确的事情,就将其删除。
拥有zsh
和zmv
% autoload zmv
% setopt expanded_glob
%
% zmv -n '(*[0-9])(*.mkv)' '${(l:2::0:)$(( $1-47 ))}$2'
mv -- '048. banana.mkv' '01. banana.mkv'
mv -- '049. apple.mkv' '02. apple.mkv'
mv -- '050. mango.mkv' '03. mango.mkv'
mv -- '051. pear.mkv' '04. pear.mkv'
mv -- '052. grape.mkv' '05. grape.mkv'
在这种情况下,数字格式化是使用l
(字母 ell)扩展修饰符完成的 - from man zshexpn
:
l:expr::string1::string2: Pad the resulting words on the left. Each word will be trun‐ cated if required and placed in a field expr characters wide.
与 一样rename
,删除-n
即可真正改变名称。
答案2
我认为起始号码并不总是这样的048
,在这种情况下,您可能需要一个可以在每个目录中运行的命令(无需修改)并且始终执行正确的事情。
您可以在每个目录中运行此命令:
file-rename -n 's/^\d{3}\./sprintf "%02d.", ++our $i/e' -- *.mkv
这类似于steeldriver 的回答,主要区别在于它会帮你确定从哪里开始。还有其他细微的差别。注意如果输入编号中有空白,这将填补输出编号中的空白,这可能是也可能不是您想要的。也就是说,如果您的文件为048. banana.mkv
、049. apple.mkv
和051. mango.mkv
,则输出将是01. banana.mkv
、02. apple.mkv
和03. mango.mkv
(不是 04. mango.mkv
)。
如上所述,由于该选项,该命令不会重命名任何文件-n
。相反,它显示了它会执行。如果您对此感到满意,您可以再次运行它而无需-n
:
file-rename 's/^\d{3}\./sprintf "%02d.", ++our $i/e' -- *.mkv
您可能没有该file-rename
命令。如果没有,那么您很可能没有rename
命令,或者(这就是我建议使用的原因file-rename
)rename
您的rename
命令可能完全不同。如果没有file-rename
(或者如果您更喜欢将其写为rename
并且没有),您可以通过安装软件包来获取它rename
:
sudo apt update && sudo apt install rename
怎么运行的
当你的 shell 将 glob 模式扩展*.mkv
为文件名列表时,结果列表按字典顺序排列。具体含义取决于您当前的语言环境,并且通常它与数字顺序不同,即使两者都适用。但是,由于您的文件名从最开始就已编号,并且所有数字都在左侧用0
s 填充,因此不会出现此问题,并且您的 shell 将扩展*.mkv
为按数字顺序排列的路径列表。
由于 shell 会处理这个问题,剩下的预防措施就是确保file-rename
避免更改任何文件不以三位数字序列开头,后跟.
。
在替换表达式中s/^\d{3}\./sprintf "%02d.", ++our $i/e
:
^\d{3}\.
是一个正则表达式。它与文件名开头 ( ) 处紧接着三个 ( ) 位数字 ( ).
之后\.
出现的文字 ( ){3}
匹配。\d
^
sprintf "%02d.", ++our $i
是 Perl 代码。它使用全局变量
$i
,如果尚未看到,则将其引入(our
)。$i
以零值开始,但每次使用时都会递增,在使用它之前立即递增(前缀++
会这样做)。因此,对于每个文件,它都采用值 1、2、3 等。sprintf
用于格式化它,如果需要的话,在左边填充零,宽度为 2,后跟文字.
. (.
只需要在正则表达式中进行转义,否则它将匹配任何字符,而不是在替换表达式中进行转义。e
最后导致表达式为评估作为 Perl 代码生成替换结果。如果没有这个e
,则以 开头的实际文本sprintf
将被替换到每个文件名中,而不是将该文本作为 Perl 代码进行求值的结果。
文件名不是以三位数字开头,后面跟着 a 的文件.
不会被重命名,也不会导致文件$i
数量增加。文件名不是以后缀结尾的,一开始.mkv
就不会被传递。file-rename
自动化
您可能只想对每个目录运行一次,因为听起来您只有少数几个目录。但是如果您想自动执行,您可以。这可以通过 来完成find
,尽管它比乍一看更棘手和更复杂,因为路径看起来像find
(例如,使用-execdir
您将获得类似 的路径./048. banana.mkv
),并且因为/-exec
会拆分为多个命令(不希望重置)在目录非常大的情况下(您没有,但这是另一个必须考虑的因素)。-execdir
+
$i
因此您可能只想使用 shell 循环来运行它:
for d in dir1 dir2; do
printf '%s:\n' "$d"
(cd -- "$d" && file-rename -n 's/^\d{3}\./sprintf "%02d.", ++our $i/e' -- *.mkv)
done
(这对于包含大量文件的目录也不起作用*.mkv
,但它会为这些目录提供错误消息,而不是执行错误的操作。)
替换dir1 dir2
为包含要重命名的文件的实际目录列表。
这将产生如下输出:
dir1:
rename(048. banana.mkv, 01. banana.mkv)
rename(049. apple.mkv, 02. apple.mkv)
rename(050. mango.mkv, 03. mango.mkv)
rename(051. pear.mkv, 04. pear.mkv)
rename(052. grape.mkv, 05. grape.mkv)
dir2:
rename(099. foo.mkv, 01. foo.mkv)
rename(100. bar.mkv, 02. bar.mkv)
rename(101. baz.mkv, 03. baz.mkv)
如果看起来不错,您可以-n
像以前一样删除 。(您仍然会看到目录名称标题,但看不到行rename(..., ...)
。如果您希望在实际重命名期间也看到这些,您可以将选项传递-v
给file-rename
,即替换-n
为-v
,而不是仅仅删除-n
。不过,这可能会使注意到任何错误消息变得更加困难。)
进一步阅读
在 Ubuntu 中批量重命名文件;rename 命令的简要介绍(经过奥利) 展示并解释了几种使用方法
rename
,包括sprintf
。这适用于当前的 Perl 重命名器
file-rename
和旧的prename
。(在最新的 Ubuntu 版本中,file-rename
和prename
是相同的,但在较旧的版本中,它们有所不同,并且行为略有不同。)在 Ubuntu 中,rename
默认情况下 不存在或存在解析为其中一个的符号链接。它不适用于rename.ul
,这是某些系统上的另一个实用程序(如果您在 Ubuntu 上配置它),当您运行 时会运行rename
。有关 Perl 中正则表达式的信息,我建议
perldoc perlrequick
和perldoc perlre
. 这些涵盖了表达式的语法s/
。有关正则表达式的一般信息,我建议https://www.regular-expressions.info。