在linux中使用正则表达式重命名文件
Posted
技术标签:
【中文标题】在linux中使用正则表达式重命名文件【英文标题】:Rename files using regular expression in linux 【发布时间】:2012-08-02 07:48:24 【问题描述】:我有一组文件名为:
Friends - 6x03 - Tow Ross' Denial.srt
Friends - 6x20 - Tow Mac and C.H.E.E.S.E..srt
Friends - 6x05 - Tow Joey's Porshe.srt
我想像下面这样重命名它们
S06E03.srt
S06E20.srt
S06E05.srt
我应该怎么做才能在 linux 终端中完成这项工作? 我已经安装了重命名,但你使用以下内容出现错误:
rename -n 's/(\w+) - (\d1)x(\d2)*$/S0$2E$3\.srt/' *.srt
【问题讨论】:
我在另一篇文章中分享了我的解决方案:***.com/a/60969424/418599。 【参考方案1】:你忘记了星号前面的一个点:
rename -n 's/(\w+) - (\d1)x(\d2).*$/S0$2E$3\.srt/' *.srt
在 OpenSUSE、RedHat、Gentoo 上,您必须使用 rename
的 Perl 版本。 This answer 显示如何获取它。在 Arch 上,这个包被称为perl-rename
。
【讨论】:
OpenSUSE、RedHat、Gentoo 不支持rename
中的正则表达式
@mmrmartin:这里使用的重命名脚本是 Larry Wall 编写的。它曾经在文件/usr/bin/rename
中,但也许它已被重命名(不是双关语)?在 Debian 上,脚本名称现在是 /usr/bin/file-rename
。
openSUSE 使用重命名 util-linux
包,我没有找到任何提供 file-rename
、prename
或 perl-rename
的包 - 对我来说,唯一可行的解决方案是 install using cpan。
@mmrmartin 在 RHEL 6 上也有同样的问题,它也使用基于 util-linux
的 rename
。见***.com/a/48280659/1236128。【参考方案2】:
并非每个发行版都提供支持上述示例中使用的正则表达式的rename
实用程序 - RedHat、Gentoo 及其衍生产品等。
尝试使用的替代方案是 perl-rename
和 mmv
。
【讨论】:
【参考方案3】:你可以使用rnm:
rnm -rs '/\w+\s*-\s*(\d)x(\d+).*$/S0\1E\2.srt/' *.srt
解释:
-rs
:替换 /search_regex/replace_part/modifier
形式的字符串
(\d)x(\d+)
中的 (\d)
和 (\d+)
是两个捕获的组(分别为 \1
和 \2
)。
更多示例here。
【讨论】:
就像一个魅力,它还显示在采取任何行动之前文件名的转换。 【参考方案4】:编辑:找到了一种更好的方法来列出文件,而无需使用IFS
和ls
,同时仍然符合sh
。
我会为此编写一个 shell 脚本:
#!/bin/sh
for file in *.srt; do
if [ -e "$file" ]; then
newname=`echo "$file" | sed 's/^.*\([0-9]\+\)x\([0-9]\+\).*$/S0\1E\2.srt/'`
mv "$file" "$newname"
fi
done
上一个脚本:
#!/bin/sh
IFS='
'
for file in `ls -1 *.srt`; do
newname=`echo "$file" | sed 's/^.*\([0-9]\+\)x\([0-9]\+\).*$/S0\1E\2.srt/'`
mv "$file" "$newname"
done
【讨论】:
IFS='\n'
在这个例子中代表什么?我喜欢它,因为它没有使用任何特殊的东西。
IFS:内部字段分隔符,用于扩展后的分词,并使用 read 内置命令将行拆分为单词。默认值为 "\n
允许每行获取一个文件。for file in `find . -type f`; do
(但您还需要更新 sed 以捕获路径)【参考方案5】:
如果您的 linux 不提供 rename,您也可以使用以下内容:
find . -type f -name "Friends*" -execdir bash -c 'mv "$1" "$1/\w+\s*-\s*(\d)x(\d+).*$/S0\1E\2.srt"' _ \;
我经常使用这个 sn-p 在我的控制台中使用正则表达式执行替换。
我不太擅长 shell-stuff,但据我了解这段代码,它的解释是:你的 find 的搜索结果将被传递给 bash-command (bash -c),您的搜索结果将作为源文件在 $1 内。接下来的目标是子shell中替换的结果,其中 $1 的内容(这里:只是 1 在您的参数替换 1//find/replace) 也将是您的搜索结果。 将其传递给 -execdir
的内容将不胜感激更好的解释:)
请注意:我只复制粘贴了您的正则表达式;请先使用示例文件对其进行测试。根据您的系统,您可能需要将 \d 和 \w 更改为 [[:digit:]] 或 [[:alpha:]] 等字符类。但是, \1 应该适用于组。
【讨论】:
正如 bash 手册所说:“-c string 如果存在 -c 选项,则从字符串中读取命令。如果字符串后面有参数,则将它们分配给位置参数,开始$0.",所以你甚至可以改进你的命令:find . -type f -name "Friends*" -execdir bash -c 'mv "$0" "$0/\w+\s*-\s*(\d)x(\d+).*$/S0\1E\2.srt"' \;
【参考方案6】:
使用mmv(大规模移动?)
简单但有用:*
通配符匹配任何字符串(不带斜线),?
匹配要匹配的字符串中的任何字符。在替换字符串中使用#X
来引用第X 个通配符匹配。
在你的情况下:
mmv 'Friends - 6x?? - Tow *.srt' 'S06E#1#2.srt'
这里#1#2
代表??
捕获的两个数字(匹配#1 和#2)。
因此进行了以下替换:
Friends - 6x?? - Tow * .srt matches
Friends - 6x03 - Tow Ross' Denial.srt which is replaced by
↓↓
S06E03.srt
mmv
还提供[
和]
和;
的匹配。
您不仅可以批量重命名,还可以批量移动、复制、追加和链接文件。
查看man page了解更多信息!
就我个人而言,我用它来填充数字,以便编号文件在按字典顺序排序时以所需的顺序出现(例如,1 出现在 10 之前):file_?.ext
→ file_0#1.ext
【讨论】:
【参考方案7】:真的很酷 lil diddy。 find + perl + xargs + mv
xargs -n2
可以每行打印两个参数。当与 Perl 的 print $_
结合使用时(首先打印 $STDIN),它是一个强大的重命名工具。
find . -type f | perl -pe 'print $_; s/input/output/' | xargs -d "\n" -n2 mv
perl -pe 'print $_; s/OldName/NewName/' | xargs -n2
的结果是:
OldName.ext NewName.ext
OldName.ext NewName.ext
OldName.ext NewName.ext
OldName.ext NewName.ext
我的系统上没有现成的 Perl rename
。
它是如何工作的?
find . -type f
输出文件路径(或文件名......您可以在这里控制正则表达式处理的内容!)
-p
打印正则表达式处理的文件路径,-e
执行内联脚本
print $_
先打印原始文件名(独立于-p
)
-d "\n"
用换行符代替默认的空格字符来剪切输入
-n2
每行打印两个元素
mv
获取上一行的输入
【讨论】:
对我来说,这是最好的答案 - 带有开箱即用工具的 oneliner 这删除了我所有的文件。幸运的是我做了备份 最后一个命令应该改成xargs -d '\n' -n2 mv
,否则xargs会把文件名中的空格当作分隔符,要么导致错误,要么无意义地重命名文件。 -d '\n'
参数指定应将换行符视为分隔符。 GNU xargs 有 -d
参数,但对于那些没有的实现(即我使用的 FreeBSD),这将适用于大多数环境:find . -type f | perl -pe 'print $_; s/input/output/' | sed 's/ /\\ /g' xargs -n2 mv
使用 sed
转义输出中的所有空格xargs
。 (也许不优雅。)
@s.co.tt 将空格视为普通字符的更好方法是使用不同的分隔符字符。 Xargs 支持 0 字节,所以确实找到了。我会做一个find -print0
,然后是一个xargs -0
。
另一个改进是通过 grep 对查找结果进行预过滤,以最小化无操作重命名:find . -type f | grep 'input' | perl -pe 'print $_; s/input/output/' | xargs -n2 mv
【参考方案8】:
我认为最简单和通用的方法是使用for loop
sed
和mv
。
首先,您可以在管道中检查您的正则表达式替换:
ls *.srt | sed -E 's/.* ([0-9])x([0-9]2) .*(\.srt)/S\1E\2\3/g'
如果打印出正确的替换,只需将其放入 for loop
和 mv
for i in $(ls *.srt); do
mv $i $(echo $i | sed -E 's/.* ([0-9])x([0-9]2) .*(\.srt)/S\1E\2\3/g')
done
【讨论】:
以上是关于在linux中使用正则表达式重命名文件的主要内容,如果未能解决你的问题,请参考以下文章