216

我必须递归地重命名一个完整的文件夹树,这样任何地方都不会出现大写字母(它是 C++ 源代码,但这不重要)。

忽略 CVS 和 Subversion 版本控制文件/文件夹的奖励积分。首选方式是 shell 脚本,因为任何 Linux 机器上都应该有 shell。

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

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

  2. 我会考虑 AZ 字符并将它们转换为 az,其他一切都只是在要求问题(至少在源代码中)。

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

4

29 回答 29

294

更小我很喜欢:

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

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

rename -f 'y/A-Z/a-z/' *
于 2011-11-17T12:30:58.723 回答
194

使用"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")具有更大的灵活性。

于 2008-09-30T12:03:32.720 回答
107
for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
于 2008-09-30T10:36:08.637 回答
95

如果您不需要关心效率,只需尝试以下操作。

zip -r foo.zip foo/*
unzip -LL foo.zip
于 2014-08-31T08:38:36.433 回答
23

可以简单地使用以下不太复杂的方法:

rename 'y/A-Z/a-z/' *
于 2017-02-18T10:54:18.193 回答
17

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

rename --lower-case --force somedir/*
于 2015-11-09T21:08:42.557 回答
17

这适用于CentOS / Red Hat Linux或其他没有renamePerl 脚本的发行版:

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

来源:将所有文件名从大写字母重命名为小写字母

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

于 2016-04-01T15:16:06.830 回答
15

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

...好吧猜猜是什么...

于 2008-09-30T12:34:13.580 回答
13

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

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

--force 即使具有目标名称的文件已经存在,也要重命名。

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

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

于 2019-05-09T16:32:00.557 回答
5

这是我使用 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”来搜索文件夹。

于 2008-09-30T10:27:20.437 回答
5

单线:

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 开头的文件/目录,因此请进行相应调整。

于 2020-11-18T18:26:31.933 回答
4

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

于 2008-09-30T10:50:14.500 回答
4

最初的问题要求忽略 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 '{}'  \;

伊恩

于 2008-09-30T11:08:29.373 回答
4

不可移植,仅限 Zsh,但非常简洁。

首先,确保zmv已加载。

autoload -U zmv

另外,请确保extendedglob已打开:

setopt extendedglob

然后使用:

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

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

于 2013-07-18T17:16:45.627 回答
3

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

请注意第一个查找中的 -depth 操作。

于 2008-09-30T10:54:48.767 回答
3
for f in `find -depth`; do mv ${f} ${f,,} ; done

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

于 2016-02-22T18:42:51.307 回答
3

使用排版

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.

于 2017-09-29T16:09:58.943 回答
2

在 OS X 中,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
于 2015-03-25T06:25:39.747 回答
2

使用 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
于 2018-04-07T09:32:17.737 回答
2

冗长但“没有惊喜也没有安装”

该脚本处理带有空格、引号、其他不常见字符和 Unicode 的文件名,适用于不区分大小写的文件系统和大多数安装了 bash 和 awk 的 Unix-y 环境(即几乎所有)。如果有任何冲突,它还会报告冲突(将文件名保留为大写),当然还会重命名文件和目录并递归工作。最后,它具有很强的适应性:您可以调整 find 命令以定位您想要的文件/目录,您可以调整 awk 来执行其他名称操作。请注意,“处理 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

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

于 2018-09-24T12:12:21.570 回答
2

这在 macOS 上也很有效:

ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"
于 2018-12-25T01:19:10.453 回答
1

我需要在 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

)

于 2015-01-15T17:43:03.470 回答
1

Slugify 重命名(正则表达式)

这不是 OP 所要求的,而是我希望在此页面上找到的内容:

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

rename "s/[^a-zA-Z0-9\.]+/-/g" filename
于 2015-02-28T02:32:26.350 回答
1

在这种情况下,我会使用 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])
于 2016-01-31T18:07:20.520 回答
1

如果你使用Arch Linux,你可以安装rename) 包,AUR它提供了可执行的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文件;)

于 2017-06-22T21:35:24.627 回答
0
( 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 不可用),然后是文件。然后grep -v /CVS而不是find ...-prune因为它更简单。对于大型目录,for f in ...可能会溢出一些 shell 缓冲区。使用查找... | 在阅读时避免这种情况。

是的,这将破坏仅在情况下有所不同的文件......

于 2008-09-30T11:26:32.853 回答
0
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/bar./foo-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搜索和替换正则表达式中的命令输出替换的文本。

正则表达式本身由两个捕获组成:直到最后一个 / 的部分(这是文件的目录),以及文件名本身。目录保持不变,但文件名被转换为小写。因此,如果find输出./FOO/BAR,它将变为mv -n -v -T ./FOO/BAR ./FOO/bar-n选项mv确保不覆盖现有的小写文件。该-v选项会mv输出它所做的每一次更改(或不进行 - 如果./FOO/bar已经存在,它会输出类似 的内容./FOO/BAR -> ./FOO/BAR,并注意没有进行任何更改)。这里-T非常重要 - 它将目标文件视为目录。如果该目录恰好存在,这将确保./FOO/BAR不会将其移入。./FOO/bar

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


sh

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

于 2019-02-14T16:29:26.280 回答
0

这里没有一个解决方案对我有用,因为我在一个无法访问 perl 重命名脚本的系统上,而且一些文件包含空格。但是,我发现了一个有效的变体:

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

归功于“Gilles 'SO-stop be evil'”,请参阅Unix 和 Linux StackExchange 上类似问题“将整个目录树更改为小写名称”的答案

于 2020-09-16T10:00:05.077 回答
0

我相信单行可以简化:

for f in **/*; do mv "$f" "${f:l}"; done

于 2022-02-10T10:06:20.070 回答