代码之家  ›  专栏  ›  技术社区  ›  vividos

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

  •  150
  • vividos  · 技术社区  · 17 年前

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

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

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

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

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

    26 回复  |  直到 7 年前
        1
  •  158
  •   Alex B    17 年前

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

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

    这样可以避免在文件之前重命名目录,并尝试将文件移动到不存在的目录(例如 "A/A" 进入之内 "a/a" )

    或者,更详细的版本,不使用 “重命名” .

    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" )

        2
  •  231
  •   Antzi    11 年前

    我还是很喜欢小一点的

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

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

    rename -f 'y/A-Z/a-z/' *
    
        3
  •  79
  •   rubo77    10 年前
    for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
    
        4
  •  61
  •   Ginhing    11 年前

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

    zip -r foo.zip foo/*
    unzip -LL foo.zip
    
        5
  •  14
  •   niXar    17 年前

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

    …好吧,猜猜看……

        6
  •  12
  •   alemol    8 年前

    如果您已经设置了 重命名 命令(例如,通过在Mac中安装BREW):

    rename --lower-case --force somedir/*
    
        7
  •  9
  •   Stephen    9 年前

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

    重命名“y/a-z/a-z/”*

        8
  •  6
  •   rubo77    10 年前

    这在CentOS/RedHat或其他发行版上工作,没有 rename Perl脚本:

    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

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

        9
  •  4
  •   agnul    17 年前

    使用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当然是上面的脚本)

        10
  •  4
  •   Ian Dickinson    17 年前

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

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

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

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

    伊恩

        11
  •  4
  •   vividos    17 年前

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

        12
  •  3
  •   tzot    17 年前

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

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

        13
  •  2
  •   Mihai Limbășan    17 年前

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

    1. 如果路径层次结构中的同一级别上有两个或多个名称,但这些名称只因大小写而有所不同,则会发生什么情况,例如 ABCdef , abcDEF aBcDeF ?重命名脚本应该中止还是只发出警告并继续?

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

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

        14
  •  2
  •   akilesh raj    7 年前

    使用MACOS,

    安装 rename 包裹,

    brew install rename
    

    使用,

    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
    
        15
  •  1
  •   benjwadams    13 年前

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

    首先,确保 zmv 已加载。

    autoload -U zmv
    

    同时,确保 extendedglob 是:

    setopt extendedglob
    

    然后使用:

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

    以递归方式小写文件和目录,其中名称不是 并行版本系统 .

        16
  •  1
  •   Jonghee Park    11 年前

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

    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
    
        17
  •  1
  •   John Foley    10 年前

    在这种情况下,我会使用python,以避免乐观地假设没有空格或斜线的路径。我也发现了 python2 安装在比 rename .

    #!/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])
    
        18
  •  1
  •   jpaugh JotaBe    10 年前
    for f in `find -depth`; do mv ${f} ${f,,} ; done
    

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

        19
  •  0
  •   pklausner    17 年前
    ( 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
    

    首先从下到上重命名目录 排序-R (其中-depth不可用),然后是文件。 然后 GRIP-V/CVS 而不是 找到……修剪 因为它更简单。 对于大目录, 因为… 会溢出一些外壳缓冲区。 使用 发现…阅读时 为了避免这种情况。

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

        20
  •  0
  •   cwd    11 年前

    slugify重命名(regex)

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

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

    rename "s/[^a-zA-Z0-9\.]+/-/g" filename
    
        21
  •  0
  •   fedorqui    11 年前

    我需要在Windows7上的cygwin安装程序上执行此操作,并发现我遇到语法错误,上面给出了我尝试过的建议(尽管我可能错过了一个有效的选项),但是此解决方案直接来自 ubutu forums 从罐中取出:—)

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

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

    for f in *\ *; do mv "$f" "${f// /_}"; done
    
        22
  •  0
  •   w17t too much php    9 年前

    如果你使用 Arch Linux ,您可以安装 rename 包装从 AUR 提供 renamexm 命令AS /usr/bin/renamexm 可执行文件和ITS manual 随它一起翻页。

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

    转换为小写

    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 # substite string on pattern
    
    --yes # Confirm all actions
    

    你可以拿样品 开发人员MP3 文件从 here 如果需要的话;

        23
  •  0
  •   Paul Hodges    8 年前

    没人建议打字?

    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。

        24
  •  0
  •   ndemou    7 年前

    漫长但 “工作时无意外&无安装”

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

    How to convert a string to lower case in Bash?

        25
  •  0
  •   Chris    7 年前

    这也适用于MacOS:

    ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"
    
        26
  •  0
  •   oisyn    7 年前
    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 重命名 /fo ./foo 仍将继续列出 /fo/bar 即使该路径不再有效)。上面的命令对我没有任何影响。

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


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

    这将从当前目录中找到任何文件(更改 . 使用深度优先搜索(例如,它将列出 ./foo/bar 之前 /fo ,但仅适用于包含大写字符的文件。这个 -name 筛选器只应用于基本文件名,而不是完整路径。所以这将列出 /fo/bar 但不是 ./FOO/bar . 没关系,因为我们不想重命名 /fo/bar . 我们想重新命名 /fo 尽管如此,但那一个在后面列出(这就是为什么 -depth 很重要)。

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


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

    此部分读取 找到 并将其格式化为 mv 使用正则表达式的命令。这个 -n 选择停止 sed 从打印输入,和 p 命令在搜索和替换regex中输出被替换的文本。

    regex本身包含两个捕获:直到最后一个(文件的目录)的部分和文件名本身。目录保持不变,但文件名转换为小写。所以,如果 找到 输出 /fo/bar ,它将成为 mv -n -v -T ./FOO/BAR ./FOO/bar . 这个 -N 选择权 中频 确保不会覆盖现有的小写文件。这个 -v 期权制 中频 输出它所做的每一个更改(或不做的每一个更改-如果 /fo/bar 已经存在,它输出类似 ./FOO/BAR -> ./FOO/BAR 注意到没有任何变化)。这个 -T 在这里非常重要-它将目标文件视为一个目录。这将确保 /fo/bar 没有搬进 /fo/bar 如果该目录恰好存在。

    和这个一起用 找到 生成将要执行的命令列表(方便验证不实际执行的操作)


    sh
    

    这是不言自明的。它传送所有生成的 中频 对shell解释器的命令。你可以换成 bash 或者任何你喜欢的外壳。