【发布时间】:2016-10-20 13:45:10
【问题描述】:
我有数以千计的 mp3 文件,但都有不寻常的文件名,例如 1-2songone.mp3、2songtwo.mp3、2_2_3_songthree.mp3。我想删除这些文件开头的所有数字、破折号和下划线并得到结果:
songone.mp3
songtwo.mp3
songthree.mp3
【问题讨论】:
我有数以千计的 mp3 文件,但都有不寻常的文件名,例如 1-2songone.mp3、2songtwo.mp3、2_2_3_songthree.mp3。我想删除这些文件开头的所有数字、破折号和下划线并得到结果:
songone.mp3
songtwo.mp3
songthree.mp3
【问题讨论】:
Benjamin W.'s answer 有用且高效,但有两个缺点:
(shopt -s extglob; for fname ...))。
extglob 语法是常规 glob 语法的扩展,很少有人熟悉,但仍不如真正的正则表达式强大。
使用 Bash 的 regex-matching operator, =~:
for f in *.mp3; do [[ $f =~ ^[0-9_-]+(.+)$ ]] && echo mv "$f" "${BASH_REMATCH[1]}"; done
删除echo 以执行实际重命名。
$f =~ ^[0-9_-]+(.+)$ 匹配文件名开头的最长非空数字、连字符和下划线序列,后跟在带括号的子表达式(捕获组)中捕获的任何非空字符序列。
如果匹配成功 (&&),则调用 mv 命令,捕获的子表达式 - 可通过特殊 BASH 数组变量 ${BASH_REMATCH[@]} 的元素 1 访问 - 形成目标文件名。
【讨论】:
这可以使用扩展通配符来完成:
$ ls
1-2songone.mp3 2_2_3_songthree.mp3 2songtwo.mp3
$ shopt -s extglob
$ for fname in *.mp3; do mv -- "$fname" "${fname##*([-_[:digit:]])}"; done
$ ls
songone.mp3 songthree.mp3 songtwo.mp3
这使用parameter expansion:${fname##pattern} 从fname 的开头删除可能的最长匹配。作为模式,我们使用*([-_[:digit:]]),其中*(pattern)代表“模式的零个或多个匹配”,实际模式是连字符、下划线和数字的括号表达式。
备注:
mv 后面的-- 表示move 的选项结束,并确保以- 开头的文件名不会被解释为选项。
*() 表达式需要 extglob shell 选项。正如所指出的,如果您以后不想扩展 glob,则必须使用 shopt -u extglob 再次取消设置。
根据Gordon Davisson 的评论:如果您有类似1file.mp3 和2file.mp3 的文件,这将破坏文件。为避免这种情况,您可以使用mv -i(或--interactive),它会在覆盖文件之前提示您,或者使用mv -n(或--noclobber),它不会覆盖任何文件。
triplee 指出,如果文件不以斜线、下划线或数字开头,这会不必要地将文件移动到自身上。为了避免这种情况,我们只能遍历匹配的文件
for fname in [-_[:digit:]]*.mp3; do mv -- "$fname" "${fname##*([-_[:digit:]])}"; done
这确保有一些东西要重命名。
【讨论】:
mv -i 进行此类批量移动,以避免在类似名称的文件(或导致其执行意外操作的拼写错误,或...)的情况下意外丢失数据。
[-_[:digit:]]* 以避免不必要地将不匹配的文件移动到自己身上。
mv --或mv "./$fname"或for fname in ./*.mp3,以防文件名以连字符开头...
你也可以这样做:
find . -type f -name "*.mp3" -print0 | while read -r -d '' line
do
mv "$line" "$( sed -E 's!(.*)/[^[:alpha:]]*([[:alpha:]].*mp3)$!\1/\2!' <<<"$line")" 2>/dev/null
done
我猜,使用 sed 可以让您更好地控制正则表达式。此外,2>/dev/null 用于忽略已转换/正确文件名的 mv 错误。
注意:
这也将递归地更改子文件夹中的文件名。
【讨论】: