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

How to rename all folders and files to lowercase on Linux?

我必须递归地重命名一个完整的文件夹树,这样就不会有大写字母出现在任何地方(它是C++源代码,但这不重要)。忽略cvs和svn控制文件/文件夹的奖励积分。首选的方法是shell脚本,因为shell应该在任何Linux设备上都可用。

有一些关于文件重命名详细信息的有效参数。

  • 我认为具有相同小写名称的文件应该被覆盖,这是用户的问题。当在一个案例中签出时,忽略文件系统也会用后者覆盖第一个案例。

  • 我会考虑a-z字符并将其转换为a-z,其他一切都只是为了解决问题(至少是源代码)。

  • 在Linux系统上运行构建需要这个脚本,所以我认为应该忽略对cvs或svn控制文件的更改。毕竟,这只是一个临时结账。也许"出口"更合适。


  • 我还是很喜欢小一点的

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

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

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


    使用"rename"命令的简明版本。

    1
    find my_root_dir -depth -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    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.

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


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


    如果你不需要关心效率的话,只需简单地尝试跟随。

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


    上面的大多数答案都是危险的,因为它们不处理包含奇数字符的名称。对于这种情况,最安全的选择是使用find的-print0选项,它将使用ascii nul而不是终止文件名。在这里,我提交这个脚本,它只更改文件,而不更改目录名,以免混淆find。

    1
    2
    3
    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"'

    我测试过它,它与包含空格、各种引号等的文件名一起工作。这很重要,因为如果您以根目录运行树上的其他脚本之一,其中包括由以下对象创建的文件:

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

    …好吧,猜猜看……


    如果您已经有或设置了RENAME命令(例如,通过在Mac中安装BREW),则可以使用此命令:

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


    伙计,你们喜欢把事情搞得过于复杂……

    重命名"y/a-z/a-z/"*


    这适用于CentOS/RedHat或其他发行版,不使用renamePerl脚本:

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

    来源:https://linuxconfig.org/rename-all-files-from-uppercase-to-lowercase-characters

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


    使用Larry Wall的文件名修复程序

    1
    2
    3
    4
    5
    6
    7
    8
    $op = shift or die $help;
    chomp(@ARGV = <STDIN>) unless @ARGV;
    for (@ARGV) {
        $was = $_;
        eval $op;
        die $@ if $@;
        rename($was,$_) unless $was eq $_;
    }

    这很简单

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

    (fix当然是上面的脚本)


    原始问题要求忽略svn和cvs目录,这可以通过向find命令添加-prune来完成。例如,忽略cvs:

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

    [编辑]我尝试了这个方法,但由于我不太明白的原因,在搜索结果中嵌入小写翻译不起作用。因此,将此修改为:

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

    伊恩


    下面是我的次优解决方案,使用bash shell脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #!/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"搜索文件夹。


    这是一个小的shell脚本,执行您的请求:

    1
    2
    3
    4
    5
    6
    7
    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

    注意第一个查找中的深度操作。


    使用MACOS,

    安装rename包,

    1
    brew install rename

    使用,

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

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

    1
    `f` - forces a rename

    例如,

    1
    2
    3
    4
    5
    6
    7
    $ 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


    以前发布的将完全不受限制地工作,或者对简单情况进行一些调整,但是在运行批重命名之前,您可能需要考虑一些情况:

  • 如果在路径层次结构中有两个或多个相同级别的名称(仅因情况而不同),如ABCdefABCdefABCdef,会发生什么情况?重命名脚本应该中止还是只发出警告并继续?

  • 如何定义非US-ASCII名称的小写?如果可能存在这样的名称,应该首先执行一次检查和排除传递吗?

  • 如果在cvs或svn工作副本上运行重命名操作,则如果更改文件名或目录名的大小写,可能会损坏工作副本。脚本是否还应查找和调整内部管理文件,如.svn/entries或cvs/entries?


  • 不可移植,仅限zsh,但相当简洁。

    首先,确保加载了zmv

    1
    autoload -U zmv

    另外,确保extendedglob打开:

    1
    setopt extendedglob

    然后使用:

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

    以递归方式小写文件和目录,其中名称不是cvs。


    在OSX中,mv-f显示"相同文件"错误,所以我重命名了两次。

    1
    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

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

    find -depth打印每个文件和目录,目录的内容打印在目录本身之前。${f,,}lower会显示文件名。


    在这种情况下,我会使用python,以避免乐观地假设没有空格或斜线的路径。我还发现,与rename相比,python2的安装位置更多。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #!/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])


    1
    2
    3
    4
    ( 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…会溢出一些外壳缓冲区。使用查找…|同时阅读以避免这种情况。

    是的,这将删除不同的文件,只有在案件…


    slugify重命名(regex)

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

    用于重命名文件的"slugify"版本,因此它们与URL类似(即仅包括字母数字、点和破折号):

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


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

    我没有尝试过这里提到的更复杂的脚本,但是在我的滑膜NAS上,没有一个命令行版本对我有用。rename不可用,find的许多变体都失败了,因为它似乎坚持已重命名路径的旧名称(例如,如果它发现./FOO后接./FOO/BAR,则将./FOO重命名为./FOO仍将继续列出./FOO/BAR,即使该路径不再有效)。上面的命令对我没有任何影响。

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

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

    这将使用深度优先搜索(例如,它将在./FOO之前列出./FOO/BAR)来查找当前目录中的任何文件(将.更改为要处理的任何目录),但仅限于包含大写字符的文件。-name过滤器仅适用于基本文件名,而不适用于完整路径。因此,这将列出./FOO/BAR,而不是./FOO/BAR。这没关系,因为我们不想重命名./FOO/BAR。不过,我们想重新命名./FOO,但该名称在后面列出(这就是-depth很重要的原因)。

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

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

    本部分读取find输出的文件,并使用正则表达式将其格式化为mv命令。-n选项阻止sed打印输入,搜索和替换regex中的p命令输出替换的文本。

    regex本身包含两个捕获:直到最后一个(文件的目录)的部分和文件名本身。目录保持不变,但文件名转换为小写。因此,如果find输出./FOO/BAR,它将成为mv -n -v -T ./FOO/BAR ./FOO/barmv-n选项确保不会覆盖现有的小写文件。-v选项使mv输出它所做的每一个更改(或不做的每一个更改-如果./FOO/BAR已经存在,它输出类似./FOO/BAR -> ./FOO/BAR的内容,注意到没有做任何更改)。-T在这里非常重要,它将目标文件视为一个目录。这将确保如果该目录恰好存在,则不会将./FOO/BAR移动到./FOO/BAR中。

    将它与find一起使用,生成将要执行的命令列表(方便验证将要执行的操作而不实际执行)

    1
    sh

    这是不言自明的。它将所有生成的mv命令路由到shell解释器。你可以用bash或者任何你喜欢的外壳来代替它。


    如果使用arch linux,则可以从AUR安装rename包,该包提供renamexm命令作为/usr/bin/renamexm可执行文件,并随附其手册页。

    它是快速重命名文件和目录的强大工具。

    转换为小写

    1
    rename -l Developers.mp3 # or --lowcase

    转换为大写

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

    其他选项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    -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 # substite string on pattern

    --yes # Confirm all actions

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

    < /块引用>


    没人建议打字?

    1
    2
    3
    4
    5
    typeset -l new        # always lowercase
    find $topPoint |      # not using xargs to make this more readable
      while read old
      do mv"$old""$new" # quotes for those annoying embedded spaces
      done

    在类似Git-Bash的Windows仿真中,这可能会失败,因为Windows在引擎盖下不区分大小写。对于这些,首先添加一个步骤,将mv作为文件名,比如"$old.tmp",然后添加到$new。


    冗长但"工作时不会有意外,也不会安装"

    此脚本处理带有空格、引号、其他不寻常字符和Unicode的文件名,适用于不区分大小写的文件系统和大多数安装了bash和awk的Unix-Y环境(即几乎所有)。它还报告冲突(如果有)(将文件名保留为大写),当然,重命名两个文件和目录并以递归方式工作。最后,它具有很强的适应性:您可以调整find命令以针对您希望的文件/目录,还可以调整awk以执行其他名称操作。注意,"handles unicode"的意思是它确实会转换它们的情况(不要像使用tr的答案那样忽略它们)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 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:

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


    我需要在Windows7上的cygwin设置上执行此操作,并发现我在上面给出的建议中出现语法错误(尽管我可能错过了一个工作选项),但是这个直接从ubutu论坛获得的解决方案在can中成功了:-)

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

    (注意,我以前用下划线替换过空格

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