我必须递归地重命名一个完整的文件夹树,这样就不会出现大写字母(这是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.


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


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

使用Larry Wall的文件名修复器:

$op = shift or die $help;
chomp(@ARGV = <STDIN>) unless @ARGV;
for (@ARGV) {
    $was = $_;
    eval $op;
    die $@ if $@;
    rename($was,$_) unless $was eq $_;
}

这很简单

find | fix 'tr/A-Z/a-z/'

(其中fix当然是上面的脚本)


这是一个小的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动作。


最初的问题要求忽略SVN和CVS目录,这可以通过在find命令中添加-prune来实现。例如忽略CVS:

find . -name CVS -prune -o -exec mv '{}' `echo {} | tr '[A-Z]' '[a-z]'` \; -print

[编辑]我尝试了一下,在find中嵌入小写翻译并没有起作用,原因我实际上不明白。因此,将其修改为:

$> cat > tolower
#!/bin/bash
mv $1 `echo $1 | tr '[:upper:]' '[:lower:]'`
^D
$> chmod u+x tolower 
$> find . -name CVS -prune -o -exec tolower '{}'  \;

Ian


( 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…|在读取时避免这种情况。

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


使用"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

P.S.

后者允许更灵活地使用move命令(例如,“svn mv”)。


上面的大多数答案都很危险,因为它们不处理包含奇数字符的名称。对于这种情况,最安全的方法是使用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"'

我对它进行了测试,它适用于包含空格、各种引号等的文件名。这很重要,因为如果您以根用户身份运行树中的其他脚本,其中包括由

touch \;\ echo\ hacker::0:0:hacker:\$\'\057\'root:\$\'\057\'bin\$\'\057\'bash

... 你猜怎么着……


更小的,我很喜欢:

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

在不区分大小写的文件系统上,比如OS X的HFS+,你会想要添加-f标志:

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

不能移植,只能使用Zsh,但是非常简洁。

首先,确保加载zmv。

autoload -U zmv

同时,确保extendedglob是打开的:

setopt extendedglob

然后使用:

zmv '(**/)(*)~CVS~**/CVS' '${1}${(L)2}'

递归到名称不是CVS的小写文件和目录。


如果你不需要关心效率,就试试下面的方法。

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

我需要在Windows 7上的Cygwin设置上这样做,并发现我在上面尝试的建议中出现了语法错误(尽管我可能错过了一个工作选项)。然而,这个解决方案直接来自Ubuntu论坛:-)

ls | while read upName; do loName=`echo "${upName}" | tr '[:upper:]' '[:lower:]'`; mv "$upName" "$loName"; done

(注意:我之前用下划线替换了空格:

for f in *\ *; do mv "$f" "${f// /_}"; done

)


重命名(正则)

这并不是OP所要求的,但我希望在这一页上找到:

一个“slugify”版本用于重命名文件,使它们类似于url(即只包括字母数字,点和破折号):

rename "s/[^a-zA-Z0-9\.]+/-/g" filename

在OS X中,mv -f显示“same file”错误,所以我重命名两次:

for i in `find . -name "*" -type f |grep -e "[A-Z]"`; do j=`echo $i | tr '[A-Z]' '[a-z]' | sed s/\-1$//`; mv $i $i-1; mv $i-1 $j; done

如果你已经设置了重命名命令(例如,在Mac中通过brew install),这是有效的:

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

在这种情况下,我会使用Python,以避免乐观地假设没有空格或斜杠的路径。我还发现python2往往被安装在更多的地方,而不是重命名。

#!/usr/bin/env python2
import sys, os

def rename_dir(directory):
  print('DEBUG: rename('+directory+')')

  # Rename current directory if needed
  os.rename(directory, directory.lower())
  directory = directory.lower()

  # Rename children
  for fn in os.listdir(directory):
    path = os.path.join(directory, fn)
    os.rename(path, path.lower())
    path = path.lower()

    # Rename children within, if this child is a directory
    if os.path.isdir(path):
        rename_dir(path)

# Run program, using the first argument passed to this Python script as the name of the folder
rename_dir(sys.argv[1])

for f in `find -depth`; do mv ${f} ${f,,} ; done

Find -depth打印每个文件和目录,目录的内容在目录本身之前打印。${f,,}将文件名小写。


这适用于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,这是一个不同的、不兼容的工具。)


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

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

如果你使用Arch Linux,你可以从AUR安装rename)包,它提供了renamexm命令作为/usr/bin/renamexm可执行文件和一个手册页面。

它是一个非常强大的工具,可以快速重命名文件和目录。

转换为小写字母

rename -l Developers.mp3 # or --lowcase

转换为大写

rename -u developers.mp3 # or --upcase, long option

其他选项

-R --recursive # directory and its children

-t --test # Dry run, output but don't rename

-o --owner # Change file owner as well to user specified

-v --verbose # Output what file is renamed and its new name

-s/str/str2 # Substitute string on pattern

--yes # Confirm all actions

如果需要,您可以从这里获取示例Developers.mp3文件;)


使用排版:

typeset -l new        # Always lowercase
find $topPoint |      # Not using xargs to make this more readable
  while read old
  do new="$old"       # $new is a lowercase version of $old
     mv "$old" "$new" # Quotes for those annoying embedded spaces
  done

在Windows上,像Git Bash这样的模拟可能会失败,因为Windows在本质上不区分大小写。对于这些,添加一个步骤,mv的文件首先到另一个名称,如“$old.tmp”,然后到$new。


MacOS,

安装重命名包,

brew install rename

Use,

find . -iname "*.py" -type f | xargs -I% rename -c -f  "%"                       

该命令查找所有扩展名为*.py的文件,并将文件名转换为小写。

`f` - forces a rename

例如,

$ find . -iname "*.py" -type f
./sample/Sample_File.py
./sample_file.py
$ find . -iname "*.py" -type f | xargs -I% rename -c -f  "%"
$ find . -iname "*.py" -type f
./sample/sample_file.py
./sample_file.py

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

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中转换字符串为小写?


这在macOS上也能很好地工作:

ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"

find . -depth -name '*[A-Z]*'|sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'|sh

我没有尝试过这里提到的更详细的脚本,但是在我的Synology NAS上,没有一个命令行版本适合我。rename是不可用的,并且find的许多变体失败,因为它似乎坚持已经重命名的路径的旧名称(例如,如果它找到。/FOO后面跟着。/FOO/BAR,重命名。/FOO到。/FOO仍然会继续列出。/FOO/BAR,即使该路径不再有效)。以上命令对我来说没有任何问题。

下面是该命令每个部分的解释:


find . -depth -name '*[A-Z]*'

这将找到当前目录中的任何文件(更改。到您想要处理的任何目录),使用深度优先搜索(例如。,它会在./foo之前列出./foo/bar),但只针对包含大写字符的文件。name筛选器只应用于基本文件名,而不应用于完整路径。这里会列出。/FOO/BAR而不是。/FOO/BAR。这是可以的,因为我们不想重命名。/FOO/bar。虽然我们想重命名./FOO,但后面会列出这个(这就是-depth很重要的原因)。

这个命令本身对于首先查找要重命名的文件特别有用。在完全重命名命令之后使用此命令可以搜索由于文件名冲突或错误而仍然没有被替换的文件。


sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'

该部分读取find命令输出的文件,并使用正则表达式在mv命令中格式化它们。-n选项停止sed打印输入,搜索和替换正则表达式中的p命令输出替换后的文本。

The regex itself consists of two captures: the part up until the last / (which is the directory of the file), and the filename itself. The directory is left intact, but the filename is transformed to lowercase. So, if find outputs ./FOO/BAR, it will become mv -n -v -T ./FOO/BAR ./FOO/bar. The -n option of mv makes sure existing lowercase files are not overwritten. The -v option makes mv output every change that it makes (or doesn't make - if ./FOO/bar already exists, it outputs something like ./FOO/BAR -> ./FOO/BAR, noting that no change has been made). The -T is very important here - it treats the target file as a directory. This will make sure that ./FOO/BAR isn't moved into ./FOO/bar if that directory happens to exist.

使用此命令和find命令一起生成将执行的命令列表(可以方便地验证将执行什么而无需实际执行)


sh

这是不言自明的。它将所有生成的mv命令路由到shell解释器。您可以将其替换为bash或任何您喜欢的shell。


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

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

——force即使目标文件名已经存在,也要重命名。 ——lower-case将文件名全部转换为小写。 替换文件名中所有空格序列为单个下划线字符。


这里的解决方案都不适合我,因为我所在的系统无法访问perl重命名脚本,而且有些文件包含空格。然而,我发现了一个有效的变体:

find . -depth -exec sh -c '
    t=${0%/*}/$(printf %s "${0##*/}" | tr "[:upper:]" "[:lower:]");
    [ "$t" = "$0" ] || mv -i "$0" "$t"
' {} \;

归功于“Gilles 'SO-停止作恶”,在Unix和Linux StackExchange上看到类似问题“将整个目录树更改为小写名称”的答案。


一行程序:

for F in K*; do NEWNAME=$(echo "$F" | tr '[:upper:]' '[:lower:]'); mv "$F" "$NEWNAME"; done

甚至:

for F in K*; do mv "$F" "${F,,}"; done

注意,这将只转换以字母K开头的文件/目录,因此要相应地进行调整。


我相信这些一行代码可以简化为:

对于f在**/*;做mv "$f" "${f:l}";完成


使用bash,不重命名:

find . -exec bash -c 'mv $0 ${0,,}' {} \;