如何在Linux上将所有文件夹和文件重命名为小写?

243

我需要递归重命名一个完整的文件夹树,以便任何地方都没有大写字母出现(这是C++源代码,但不应该影响)。

如果忽略CVS和Subversion版本控制文件/文件夹将获得额外奖励。最好的方法是使用shell脚本,因为在任何Linux系统中都应该有可用的shell。

有一些关于文件重命名细节的有效论点。

  1. 我认为相同小写名称的文件应该被覆盖;这是用户的问题。当在不区分大小写的文件系统上检出时,它也会将前一个文件覆盖为后一个文件。

  2. 我会考虑A-Z字符并将它们转换为a-z,其他所有内容只会引发问题(至少对于源代码而言)。

  3. 脚本需要在Linux系统上运行构建,因此我认为应省略对CVS或Subversion版本控制文件的更改。毕竟,这只是一个临时检出。也许"export"更合适。


先前发布的内容对于简单情况可以直接使用或稍作调整即可完美运行,但在批量重命名之前,有一些情况需要考虑:1. 如果在路径层次结构中存在两个或多个仅大小写不同的名称(例如ABCdefabcDEFaBcDeF),应该发生什么? 重命名脚本是否应该中止或只是警告并继续执行?2. 如何为非美国ASCII名称定义小写字母?如果可能存在这样的名称,是否应首先进行检查和排除操作?3. 如果您正在运行重命名操作 - Mihai Limbășan
31个回答

330

更小的我非常喜欢:

rename 'y/A-Z/a-z/' *

在大小写不敏感的文件系统(例如 OS X 的 HFS+)上,您需要添加-f标志:

rename -f 'y/A-Z/a-z/' *

5
默认情况下随 Ubuntu 发布的重命名工具是 Perl 程序,有时被称为 prename。 - sleepsort
7
假设您已经拥有了perl的rename命令,但并非总是如此。例如,在我的Debian系统中,rename命令完全不同。 - Krzysztof Krasoń
16
在Arch Linux上,该程序被称为perl-rename(由同名软件包提供)。 - jaymmer - Reinstate Monica
8
这并没有回答所提出的问题,因为“rename”不是递归的。 - Cybolic
10
在OSX中,请确保你已经安装了rename。brew install rename - Nathan Hinchey
显示剩余14条评论

200
使用"rename"命令的简洁版本:
find my_root_dir -depth -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;

这样可以避免在文件夹被重命名之前出现问题,尝试将文件移动到不存在的文件夹中(例如将"A/A"移动到"a/a")。

或者,更详细的版本,不使用"rename"命令。

for SRC in `find my_root_dir -depth`
do
    DST=`dirname "${SRC}"`/`basename "${SRC}" | tr '[A-Z]' '[a-z]'`
    if [ "${SRC}" != "${DST}" ]
    then
        [ ! -e "${DST}" ] && mv -T "${SRC}" "${DST}" || echo "${SRC} was not renamed"
    fi
done

附言

后者允许使用移动命令更加灵活(例如,"svn mv")。


39
可以使用rename进行如下操作: 将命令写作:rename 'y/A-Z/a-z/' * 此命令可以将当前目录下所有文件名中的大写字母转换为小写字母。 - Tzury Bar Yochay
5
请注意,这两个版本在不区分大小写的文件系统上都无法使用。我曾将then包含的行替换为(mv "${SRC}" "${DST}.renametmp" && mv "${DST}.renametmp" "${DST}") || echo "${SRC}未被重命名" - Lloeki
6
第二种方法对于包含空格的文件在我的情况下无法正常工作(上下文:Linux,Bash)。 - dim
6
使用最新版本的rename命令时,无需使用正则表达式。完整命令为 find my_root_dir -depth -exec rename -c {} \;。如果你在大小写不敏感的文件系统上(如Mac),请添加 -f 参数到rename命令中。 - Javache
如果您在git存储库中使用此内容,请确保不要重命名.git文件夹中的任何内容。 - schmunk
显示剩余10条评论

115
for f in `find .`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done

注意:macOS兼容性需要使用点号,但在Linux上仍然有效。

3
“find” 应该替换为您用于获取要重命名的文件列表的任何命令;例如,“ls *.{C,CPP,H}”。 - JPaget
2
在OS X上无法工作,只会打印“find”命令的使用信息。似乎使用“find .”可以解决这个问题。 - emlai
4
从所有评论(答案不能再编辑)中整合的“整理过的”版本:对于找到的每个文件,执行以下操作:将其名称转换为小写字母并打印输出。 命令如下: for f in `find .`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done - romaricdrigon
@romaricdrigon - 我采用了你的版本(在macOS上),它完美地工作了。 - Carl Smith
2
有人能解释一下为什么这应该起作用吗?如果它找到./FOO和./FOO/BAR,它首先将./FOO重命名为./foo,此后它就再也找不到./FOO/BAR了。在下面输入我自己的答案,应该可以解决这个问题。 - oisyn
显示剩余5条评论

114

如果您不需要考虑效率,只需尝试以下简单的方法。

zip -r foo.zip foo/*
unzip -LL foo.zip

22
哇,这种方式相当低效。 - Artjom B.
43
假设在需要重命名文件夹中的文件时计算效率通常不是一个问题...我认为这可能是该主题中最有创意和简单的解决方案。实际上,像这样微不足道的事情并不应该像已接受的答案所展示的那样复杂,而这种解决方法更适合我想要在此类操作中投入的思考量。 +1000 - Peter M. Elias
44
您可以使用“-0”(读作零,不进行压缩)开关来加快速度,命令为zip -0 -r foo.zip foo/ - Johnny Baloney
9
我不得不更改不同目录结构中的50,000个文件名。原始接受的答案只完成了约10%的工作,需要2分钟时间,而使用“-0”压缩的方法几乎是瞬间完成的! - Peon
3
这不仅是低效的,如果没有足够的可用空间来创建临时存档,那么这个任务就会变得根本不可能。 - Victor Yarema
显示剩余9条评论

26

您可以简单地使用以下方法,这样会更加简单:

rename 'y/A-Z/a-z/' *

3
无效,它提示说小写名称的文件已经存在。 - kirhgoff
@kirhgoff 我曾经在NFS挂载时遇到过这种情况 - 请按照这里的说明操作。 - joebeeson
1
这并没有为6年前发布的相同答案增加任何内容。 - ggorlen
为了避免"已存在"错误,添加-frename -f 'y/A-Z/a-z/' * - undefined

22

我在Mac OS X上发现最简单的方法是使用http://plasmasturm.org/code/rename/包:

brew install rename
rename --force --lower-case --nows *

--force 即使存在与目标名称相同的文件,也进行重命名。

--lower-case 将文件名转换为全部小写。

--nows 将文件名中所有的空白序列替换为单个下划线字符。


21

这仅适用于单个目录,不递归。 - Bram
很酷..运行良好 - Manikandan Ram

18

如果你已经拥有或设置了重命名命令(例如通过Mac中的brew install),则此方法适用:

rename --lower-case --force somedir/*

11
如果您首先设置重命名,例如通过 brew install rename 安装。 - pdu
3
重命名“y / A-Z / a-z /”* - Stephen
6
“--lower-case”?至少在Arch Linux中不是这种情况,不幸的是。 - user4104817
3
rename 不是 Bash 内置命令,它是一个外部实用程序,语法将取决于您安装的版本。该答案不足以作为声称适用于“所有基于 Unix 的操作系统”的可移植解决方案。 - Corey Goldberg
3
刚刚检查了一下,最新的 Fedora(28)中包含的“rename”命令通过“util-linux-2.31”软件包,并没有“--lower-case”选项。 - niXar

16

以上大多数答案都是危险的,因为它们没有处理包含奇怪字符的名称。在这种情况下,最安全的选择是使用find-print0选项,该选项将使用ASCII NUL而不是\n终止文件名。

以下是一个脚本,仅更改文件而不更改目录名称,以免混淆find

find .  -type f -print0 | xargs -0n 1 bash -c \
's=$(dirname "$0")/$(basename "$0");
d=$(dirname "$0")/$(basename "$0"|tr "[A-Z]" "[a-z]"); mv -f "$s" "$d"'

我进行了测试,它可以处理包含空格、各种引号等字符的文件名。这很重要,因为如果您以root身份运行其中一个其他脚本,并且该目录树包括由

touch \;\ echo\ hacker::0:0:hacker:\$\'\057\'root:\$\'\057\'bin\$\'\057\'bash
... 嗯,你猜怎么着...

使用 rename 代替 mv 会更简单。 - Victor Yarema

6

这是我使用Bash shell脚本的次优解:

#!/bin/bash
# First, rename all folders
for f in `find . -depth ! -name CVS -type d`; do
   g=`dirname "$f"`/`basename "$f" | tr '[A-Z]' '[a-z]'`
   if [ "xxx$f" != "xxx$g" ]; then
      echo "Renaming folder $f"
      mv -f "$f" "$g"
   fi
done

# Now, rename all files
for f in `find . ! -type d`; do
   g=`dirname "$f"`/`basename "$f" | tr '[A-Z]' '[a-z]'`
   if [ "xxx$f" != "xxx$g" ]; then
      echo "Renaming file $f"
      mv -f "$f" "$g"
   fi
done

文件夹已经正确重命名,当权限不匹配时mv不会询问问题,并且CVS文件夹不会被重命名(不幸的是该文件夹内的CVS控制文件仍然被重命名)。
由于"find -depth"和"find | sort -r"都以可用的顺序返回文件夹列表进行重命名,因此我更喜欢使用"-depth"来搜索文件夹。

你的第一个查找不起作用。尝试运行 "mkdir -p A/B/C",然后再运行你的脚本。 - tzot

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