我有数千个mp3文件,但都有不寻常的文件名,例如1-2songone.mp3
,2songtwo.mp3
,2_2_3_songthree.mp3
。我想删除这些文件开头的所有数字,破折号和下划线,并得到结果:
songone.mp3
songtwo.mp3
songthree.mp3
答案 0 :(得分:5)
这可以使用扩展的globbing来完成:
$ 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)
代表&#34;模式&#34;的零或多个匹配,实际模式是hyhpens,下划线和数字的括号表达式。< / p>
<强>说明强>:
--
之后的mv
表示move
的选项结束,并确保以-
开头的文件名不会被解释为选项。
*()
表达式需要extglob
shell选项。如上所述,如果您以后不想要扩展的全局,则必须使用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
确保有重命名的内容。
答案 1 :(得分:4)
Benjamin W.'s answer有用且高效,但有两个缺点:
它需要设置全局shell选项extglob
,之后应该将其恢复到之前的值(替代方法是以创建额外进程为代价,使用子shell :(shopt -s extglob; for fname ...)
)。
extglob
语法是常规glob语法的扩展,很少有人熟悉,但仍然比真正的正则表达式更强大。
使用Bash&#39; regex-matching operator, =~
:
for f in *.mp3; do [[ $f =~ ^[0-9_-]+(.+)$ ]] && echo mv "$f" "${BASH_REMATCH[1]}"; done
删除echo
以执行实际重命名。
$f =~ ^[0-9_-]+(.+)$
匹配文件名开头的最长非数字,连字符和下划线序列,后跟括号内的子表达式(捕获组)中捕获的任何非空字符序列。
如果匹配成功(&&
),则调用mv
命令,捕获的子表达式 - 可通过特殊BASH数组变量{{1}的元素1
访问} - 形成目标文件名。
答案 2 :(得分:0)
您也可以这样做:
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
错误。
注意:强>
这将以递归方式更改子文件夹之间的文件名。