在Unix中基于模式重命名多个文件

330

目录中有多个以前缀fgh开头的文件,例如:

fghfilea
fghfileb
fghfilec

我想将它们所有重命名为以前缀 jkl 开头。是否有一种单个命令可以代替逐个重命名每个文件?


2
请在此处查看:http://theunixshell.blogspot.com/2013/01/bulk-renaming-of-files-in-unix.html - Vijay
相关问题:基于多个模式重命名文件的更好方法 - Michaël Le Barbier
26个回答

364

有几种方法,但使用 rename 可能是最简单的。

使用 rename 的一种版本(Perl 的 rename):

rename 's/^fgh/jkl/' fgh*

使用另一个版本的rename(与Judy2K的答案相同):

rename fgh jkl fgh*

您应该查看您所使用平台的man手册,以了解上述哪种适用。


16
+1 我甚至不知道有重命名这个功能......现在我可以停止使用for循环以及mv和sed命令了......谢谢! - balpha
2
你链接到了一个不同的 rename,而你展示的语法是 http://unixhelp.ed.ac.uk/CGI/man-cgi?rename 的另一个。 - Hasturkun
8
并非所有的*nix系统都有此功能。例如Mac OS X上就没有,而且fink也没有相应的包可以获取。我还没有查看过MacPorts是否有此功能。 - dmckee --- ex-moderator kitten
7
据我所知,“rename”似乎是一个Linux特定的脚本或实用程序。如果您关心可移植性,请继续使用“sed”和循环或内联Perl脚本。 - D.Shawley
50
在苹果电脑上使用命令 brew install rename 安装重命名工具 :) - sam
显示剩余18条评论

146
这是如何使用sedmv一起重命名的方法:
for f in fgh*; do mv "$f" $(echo "$f" | sed 's/^fgh/jkl/g'); done

如下评论所述,如果文件名中含有空格,则可能需要在返回文件名的子函数周围加上引号才能将文件移动到指定位置:

for f in fgh*; do mv "$f" "$(echo $f | sed 's/^fgh/jkl/g')"; done

2
非常接近。请注意,您只想匹配fgh的第一次出现:'s/^fgh/jkl/g'(插入符号起到了关键作用)。 - Stephan202
2
仅仅是为了精确...你的意思是“名称开头的fgh”,而不是“第一次出现的fgh”。 /^fgh/ 可以匹配到 "fghi",但是匹配不到 "efgh"。 - Dave Sherohman
5
如果您无法访问“rename”,则此代码非常有效。如果文件名包含空格,则代码可能需要引号。for f in fgh*; do mv "$f" "$(echo $f | sed 's/^fgh/jkl/g')"; done - Dave Nelson
1
@nik,不加引号重命名这个文件列表会抛出错误:touch fghfilea fghfileb fghfilec fghfile\ d。我建议您考虑一下@DaveNelson的意见。 - luissquall
1
如果你只想匹配第一次出现,那么也不需要使用 g 修饰符。 - Seppo Enarvi
显示剩余5条评论

92

rename 命令可能不在所有系统中都存在。所以如果你没有它,可以使用 shell 来完成重命名操作。 下面是在 bash shell 中的一个示例:

for f in fgh*; do mv "$f" "${f/fgh/xxx}";done

3
在这个解决方案中,不需要使用 sed 命令。这个比 @nik 的答案更简单。 - Halil
2
xxx代表替换?对于原帖作者来说,那将是“jkl”。 - Awinad
4
它们所有解决方案中最干净的一种。 - M T Head
7
也许更强调一下这是 Bash 的扩展功能,而在 POSIX sh 或其他常见的 shell 中并不存在。 - tripleee
1
太好了 - Unix世界有很多不为人知的角落 - 以前从未见过这个。 - wcochran
1
谢谢! 我按照以下需求使用它:for f in ls; do mv $f $f.txt; done而且不需要安装任何东西。 - Sachin Warke

46

使用mmv

mmv "fgh*" "jkl#1"

1
哇,这是解决问题的一种优秀而简单的方式!很高兴被介绍给mmv,谢谢! - Hendeca
1
谢谢!以前从未听说过mmv。刚刚安装并试用了一下——简单而强大。 - Nick Rice
3
如果你想要递归地批量重命名,请在 #1 前加上 ;。例如:mmv ";fgh*" "#1jkl#2" - Alp
非常优雅的解决方案! - Fermat's Little Student
1
正是我所需要的。使用“”捕获组,并使用#1,#2等进行回调。例如:mmv“my show ep * 1080p。”“my.show.#1.#2”= my.show.001.avi - Lundy
显示剩余2条评论

22

有很多种方法(并非所有这些方法都适用于所有unix系统):

  • ls | cut -c4- | xargs -I§ mv fgh§ jkl§

    §可能会被任何你觉得方便的字符替换。你也可以使用find -exec,但许多系统的行为不同,因此我通常避免使用它。

  • for f in fgh*; do mv "$f" "${f/fgh/jkl}";done

    粗略但有效,正如人们所说

  • rename 's/^fgh/jkl/' fgh*

    非常美观,但是BSD上没有rename,而那是我所知道的最常见的unix系统。

  • rename fgh jkl fgh*

  • ls | perl -ne 'chomp; next unless -e; $o = $_; s/fgh/jkl/; next if -e; rename $o, $_';

    如果您坚持使用Perl,但是您的系统上没有rename,则可以使用此巨兽。

其中一些有点复杂,列表远未完整,但是你几乎可以在所有unix系统中找到你想要的内容。


2
我喜欢这种怪物! - lefakir
是的,我对 Perl 仍有一份情感 :) - iwein
1
注意:不应解析或管道化 ls 输出。 - phuclv
如果你的文件名中有空格,这里的 Perl 怪物恰好可以完美运行。 - mlerley

18
rename fgh jkl fgh*

9
在我的电脑上运行会产生错误:“在启用了“strict subs”选项的情况下,不允许使用裸字“fgh”,位于(eval 1)第1行。” - Stephan202
@Stephan202,你的机器是什么? - nik
Ubuntu 8.10(perl v5.10.0 / 2009-06-26) - Stephan202
@Stephan202 我有同样的问题,你解决了吗?(7年后) - whossname
1
@whossname:抱歉,我真的记不起来了。(因为假期回复较慢。) - Stephan202
显示剩余2条评论

16

使用findxargssed

find . -name "fgh*" -type f -print0 | xargs -0 -I {} sh -c 'mv "{}" "$(dirname "{}")/`echo $(basename "{}") | sed 's/^fgh/jkl/g'`"'

这种方法比@nik的解决方案更加复杂,但它可以递归地重命名文件。例如,结构如下:

.
├── fghdir
│   ├── fdhfilea
│   └── fghfilea
├── fghfile\ e
├── fghfilea
├── fghfileb
├── fghfilec
└── other
    ├── fghfile\ e
    ├── fghfilea
    ├── fghfileb
    └── fghfilec

将会被转化为这样,

.
├── fghdir
│   ├── fdhfilea
│   └── jklfilea
├── jklfile\ e
├── jklfilea
├── jklfileb
├── jklfilec
└── other
    ├── jklfile\ e
    ├── jklfilea
    ├── jklfileb
    └── jklfilec

xargs 一起使用的关键是要从 xargs 中调用 shell


3
我本来想发这样的帖子,但是要花一个小时才能找到正确的命令。 - Ruan Mendes
这个答案允许的递归使其非常强大 - 我只需快速重命名深度嵌套层次结构中的数千个文件。 - kevinmicke
1
这应该是预期的答案。 - inix
不,这个命令很危险。天啊,请不要这样做。它会把你的目录搞得一团糟。 - DawnSong

5

通用命令如下:

find /path/to/files -name '<search>*' -exec bash -c 'mv $0 ${0/<search>/<replace>}' {} \;

在上述命令中,需要将<search><replace>替换为你的源文件名和目标文件名。

对于您的问题,以下是一个更具体的示例(应当在存储文件的同一文件夹中运行):

find . -name 'gfh*' -exec bash -c 'mv $0 ${0/gfh/jkl}' {} \;

若要进行 "试运行",请在 mv 前添加echo,以便查看生成的命令:

find . -name 'gfh*' -exec bash -c 'echo mv $0 ${0/gfh/jkl}' {} \;


3
安装 Perl rename 脚本的方法:
sudo cpan install File::Rename

在Stephan202的回答中提到了两种重命名。基于Debian的发行版有Perl重命名。Redhat/rpm发行版有C重命名
OS X默认情况下没有安装任何一个(至少在10.8版本中),Windows/Cygwin也是如此。


3

以下是使用命令行Groovy实现的方法:

groovy -e 'new File(".").eachFileMatch(~/fgh.*/) {it.renameTo(it.name.replaceFirst("fgh", "jkl"))}'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接