如何通过在文件名中的任意位置填充数字来重命名文件?

如何通过在文件名中的任意位置填充数字来重命名文件?

我在 Deepin(基于 Debian)系统上有一个目录,其中包含视频文件,如下所示:

SL Benfica - Match 1 vs FC Porto.mp4
SL Benfica - Match 2 vs FC Porto.mp4
...
SL Benfica - Match 20 vs FC Porto.mp4
...
SL Benfica - Match 100 vs FC Porto.mp4
SL Benfica - Match 101 vs FC Porto.mp4

我想在中间的数字之前添加零,以便它们按如下方式排序

SL Benfica - Match 001 vs FC Porto.mp4
SL Benfica - Match 002 vs FC Porto.mp4
...
SL Benfica - Match 020 vs FC Porto.mp4
...
SL Benfica - Match 100 vs FC Porto.mp4
SL Benfica - Match 101 vs FC Porto.mp4

我正在寻找不依赖于模式的通用命令。只需搜索 5 和 20 这样的数字,并将它们更改为 005 和 020,文件名中的任何位置,甚至在文件名的末尾或开头。

答案1

您可以使用perl-rename(应该可以在sudo apt install rename基于 Debian 的系统上安装)。使用这些文件作为输入:

$ ls -1
'anotherFile.m4a'    
'file 1 with 12 many 100 numbers 3.mp4'
'SL Benfica - Match 101 vs FC Porto.mp4'
'SL Benfica - Match 20 vs FC Porto.mp4'
'SL Benfica - Match 2 vs FC Porto.mp4'

你可以运行:

rename 's/(\d+)(?=.*\.)/sprintf("%03d",$1)/eg' *

这会将它们重命名为:

'anotherFile.m4a'
'file 001 with 012 many 100 numbers 003.mp4'
'SL Benfica - Match 002 vs FC Porto.mp4'
'SL Benfica - Match 020 vs FC Porto.mp4'
'SL Benfica - Match 101 vs FC Porto.mp4'

请注意如何100保持不变,其余部分被填充。另请注意 4 中的.m4amp4并未受到影响。

重要的:先运行命令,-n看看在实际重命名文件之前会发生什么:

rename -n 's/(\d+)(?=.*\.)/sprintf("%03d",$1)/eg' *

正则表达式查找\d+至少一位.( ) 之前的一位或多位数字 ( ) ?=.*\.。这是为了避免更改分机中找到的号码。但是,如果您的文件名没有扩展名,则这将不起作用。如果您有这样的文件,请使用它来填充所有数字:

rename 's/(\d+)/sprintf("%03d",$1)/eg' *

s/old/new/替换运算符,将替换oldnew。这里,因为(\d+)是在括号中,所以无论匹配什么都会被捕获$1然后将在替换的右侧可用。因此,我们将数字替换为 0 填充的数字(sprintf("%03d", $number)将打印$number用 0 填充的数字,直到其长度为 3)。最后,该标志e允许我们使用表达式(此处为sprintfg并使替换成为全局的,适用于输入行(文件名)上的所有匹配项。

请注意,如果您的数字已经用 0 填充了超过 3 个 0,则这会将其修剪为 3-0 填充。于是file 00000001.mp3就会变成file 001.mp3

答案2

zsh

autoload -Uz zmv # best in ~/.zshrc
zmv -n '(*).mp4' '${1//(#m)<->/${(l[3][0])MATCH}}.mp4'

如果满意,请删除-n(试运行)。

  • zmv根据 zsh glob 模式重命名文件。这里(*).mp4匹配以 结尾的文件名.mp4,前面的部分.mp4被捕获因此可以像$1替换一样使用。
  • ${1//pattern/replacement}:ksh 运算符对参数(此处$1)扩展执行替换。
  • pattern(#m)<->一个 zshextendedglob模式。(#m)使匹配的文本可用,就像$MATCH替换中一样, 是数字匹配运算符<->的最简单形式,匹配 1 个或多个十进制数字的任何序列。<x-y>与...一样[0-9]##
  • ${(l[3][0])param}使用l[length][padstring]参数扩展标志将 0 的扩展左填充$MATCH到长度 3(请注意,它还会将数字截断为长度 3)。

避免截断是可能的,尽管它变得有点复杂:

zmv -n '(*).mp4' '${1//(#m)<->/${(l[$#MATCH > 3 ? $#MATCH : 3][0])MATCH}}.mp4'

我们不是填充+截断到长度 3,而是填充+截断到长度,该长度是 3 和要填充的字符串的长度中的最大值。或者,您可以使用max()可自动加载函数中的数学函数zmathfunc

autoload -Uz zmv zmathfunc; zmathfunc
zmv -n '(*).mp4' '${1//(#m)<->/${(l[max(3, $#MATCH)][0])MATCH}}.mp4'

答案3

使用 GNU sed,您可以执行以下操作(即使没有扩展名):

sed -E \
-e 'h;s/(.*)(\.[^.]*$)/\2/;ta;z;:a;x;s//\1/' \
-e 's/([0-9]{1,})/\n000\1\n/g' \
-e 's/\n[0-9]*([0-9]{3})\n/\1/g' \
-e 'G;s/\n//g' file

没有循环只有正则表达式。

第一个正则表达式将匹配

  • 'h;s/(.*)(\.[^.]*$)/\2/;ta;z;:a;x;s//\1/'将扩展部分放入保留空间(如果有)。将其余部分留在模式空间中。
  • 's/([0-9]{1,})/\n000\1\n/g'将每个数字流放在单独的行中,并在其前面添加 3 个零。

将要修改的每个数字扩展为具有前导零并位于自己的行中后,执行以下操作:

  • 's/\n[0-9]*([0-9]{3})\n/\1/g'从每行数字中提取最后 3 位数字。
  • 'G;s/\n//'重新附加在开始时捕获的扩展。

如果源列表是:

➤ cat file
1- 23.m2
ATestFile.mp4
SomeFile.m4a
AFileWithNumbers23inside.mp4
File 1 with 12 some 100 numbers 3.mp4
SL Benfica - Match 1 vs FC Porto.mp4
SL Benfica - Match 2 vs FC Porto.mp4
SL Benfica - Match 20 vs FC Porto.mp4
SL Benfica - Match 101 vs FC Porto.mp4

结果将是:

001- 023.m2
ATestFile.mp4
SomeFile.m4a
AFileWithNumbers023inside.mp4
File 001 with 012 some 100 numbers 003.mp4
SL Benfica - Match 001 vs FC Porto.mp4
SL Benfica - Match 002 vs FC Porto.mp4
SL Benfica - Match 020 vs FC Porto.mp4
SL Benfica - Match 101 vs FC Porto.mp4

因此,要重命名文件(文件名不应包含换行符,扩展名应跟随一个点):

#!/bin/bash

for old in *; do
    new=$(  printf '%s\n' "$old" | 
                sed -E \
                    -e 'h;s/(.*)(\.[^.]*$)/\2/;ta;z;:a;x;s//\1/' \
                    -e 's/([0-9]{1,})/\n000\1\n/g' \
                    -e 's/\n[0-9]*([0-9]{3})\n/\1/g' \
                    -e 'G;s/\n//'
         );
    if [[ ! -f $new ]]; then
        echo \
            mv "$old" "$new";
    fi
done

当您对脚本感到满意后,删除echo来实际更改文件。

答案4

尝试这个

$ ls SL*mp4 | awk -v mvCmd='mv "%s" "%s"\n' '{ old=$0; $5=sprintf("%3.3d",$5); printf mvCmd,old,$0 }'

如果新文件名没问题,请重做该命令,将输出通过管道传输到 bash 等 shell

该命令存储原始文件名(旧),将第五列($5)替换为 printf 的一列(三个数字,没有小数,以零开头),然后打印带有旧名称和新名称的引号的 mv(重命名)命令。

您必须将输出通过管道传输到 bash,因为 awk 本身没有文件重命名命令。

相关内容