在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-renameprenameperl-rename 的包 - 对我来说,唯一可行的解​​决方案是 install using cpan。 @mmrmartin 在 RHEL 6 上也有同样的问题,它也使用基于 util-linuxrename。见***.com/a/48280659/1236128。【参考方案2】:

并非每个发行版都提供支持上述示例中使用的正则表达式的rename 实用程序 - RedHat、Gentoo 及其衍生产品等。

尝试使用的替代方案是 perl-renamemmv

【讨论】:

【参考方案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】:

编辑:找到了一种更好的方法来列出文件,而无需使用IFSls,同时仍然符合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 内置命令将行拆分为单词。默认值为 "" -- (来自 man bash)。将其更改为\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_?.extfile_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 loopsedmv。 首先,您可以在管道中检查您的正则表达式替换:

ls *.srt | sed -E 's/.* ([0-9])x([0-9]2) .*(\.srt)/S\1E\2\3/g'

如果打印出正确的替换,只需将其放入 for loopmv

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中使用正则表达式重命名文件的主要内容,如果未能解决你的问题,请参考以下文章

linux下怎么批量重命名这些文件

使用正则表达式重命名文件名

正则表达式批量重命名 OS X 终端中的文件

在 bash 中使用正则表达式复制和重命名多个文件

使用 python 重命名文件 - 正则表达式

使用正则表达式重命名命令不起作用