我在 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 中的.m4a
和mp4
并未受到影响。
重要的:先运行命令,-n
看看在实际重命名文件之前会发生什么:
rename -n 's/(\d+)(?=.*\.)/sprintf("%03d",$1)/eg' *
正则表达式查找\d+
至少一位.
( ) 之前的一位或多位数字 ( ) ?=.*\.
。这是为了避免更改分机中找到的号码。但是,如果您的文件名没有扩展名,则这将不起作用。如果您有这样的文件,请使用它来填充所有数字:
rename 's/(\d+)/sprintf("%03d",$1)/eg' *
是s/old/new/
替换运算符,将替换old
为new
。这里,因为(\d+)
是在括号中,所以无论匹配什么都会被捕获$1
然后将在替换的右侧可用。因此,我们将数字替换为 0 填充的数字(sprintf("%03d", $number)
将打印$number
用 0 填充的数字,直到其长度为 3)。最后,该标志e
允许我们使用表达式(此处为sprintf
)g
并使替换成为全局的,适用于输入行(文件名)上的所有匹配项。
请注意,如果您的数字已经用 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 本身没有文件重命名命令。