我必须递归地重命名一个完整的文件夹树,这样就不会出现大写字母(这是c++源代码,但这无关紧要)。

忽略CVS和Subversion版本控制文件/文件夹的加分项。首选的方法是shell脚本,因为shell应该在任何Linux机器上可用。

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

I think files with the same lowercase names should be overwritten; it's the user's problem. When checked out on a case-ignoring file system, it would overwrite the first one with the latter, too. I would consider A-Z characters and transform them to a-z, everything else is just calling for problems (at least with source code). The script would be needed to run a build on a Linux system, so I think changes to CVS or Subversion version control files should be omitted. After all, it's just a scratch checkout. Maybe an "export" is more appropriate.


当前回答

冗长但“没有惊喜和装置的作品”

This script handles filenames with spaces, quotes, other unusual characters and Unicode, works on case insensitive filesystems and most Unix-y environments that have bash and awk installed (i.e. almost all). It also reports collisions if any (leaving the filename in uppercase) and of course renames both files & directories and works recursively. Finally it's highly adaptable: you can tweak the find command to target the files/dirs you wish and you can tweak awk to do other name manipulations. Note that by "handles Unicode" I mean that it will indeed convert their case (not ignore them like answers that use tr).

# adapt the following command _IF_ you want to deal with specific files/dirs
find . -depth -mindepth 1 -exec bash -c '
  for file do
    # adapt the awk command if you wish to rename to something other than lowercase
    newname=$(dirname "$file")/$(basename "$file" | awk "{print tolower(\$0)}")
    if [ "$file" != "$newname" ] ; then
        # the extra step with the temp filename is for case-insensitive filesystems
        if [ ! -e "$newname" ] && [ ! -e "$newname.lcrnm.tmp" ] ; then
           mv -T "$file" "$newname.lcrnm.tmp" && mv -T "$newname.lcrnm.tmp" "$newname" 
        else
           echo "ERROR: Name already exists: $newname"
        fi
    fi    
  done
' sh {} +

参考文献

我的剧本是基于以下这些精彩的回答:

https://unix.stackexchange.com/questions/9496/looping-through-files-with-spaces-in-the-names

如何在Bash中转换字符串为小写?

其他回答

这适用于CentOS/Red Hat Linux或其他发行版,无需重命名Perl脚本:

for i in $( ls | grep [A-Z] ); do mv -i "$i" "`echo $i | tr 'A-Z' 'a-z'`"; done

源:重命名所有文件名称从大写到小写字符

(在某些发行版中,默认的重命名命令来自util-linux,这是一个不同的、不兼容的工具。)

( find YOURDIR -type d | sort -r;
  find yourdir -type f ) |
grep -v /CVS | grep -v /SVN |
while read f; do mv -v $f `echo $f | tr '[A-Z]' '[a-z]'`; done

首先自底向上重命名目录sort -r(其中-depth不可用),然后是文件。 然后grep -v /CVS而不是find…-修剪,因为这样更简单。 对于大目录,对于f在…可以溢出一些shell缓冲区。 使用find…|在读取时避免这种情况。

是的,这将打击只有在情况下不同的文件…

这是一个小的shell脚本,做你所要求的:

root_directory="${1?-please specify parent directory}"
do_it () {
    awk '{ lc= tolower($0); if (lc != $0) print "mv \""  $0 "\" \"" lc "\"" }' | sh
}
# first the folders
find "$root_directory" -depth -type d | do_it
find "$root_directory" ! -type d | do_it

注意第一个find中的-depth动作。

你可以简单地使用下面的简单方法:

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

下面是我的次优解决方案,使用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”来搜索文件夹。