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

243

我需要递归重命名一个完整的文件夹树,以便任何地方都没有大写字母出现(这是C++源代码,但不应该影响)。

如果忽略CVS和Subversion版本控制文件/文件夹将获得额外奖励。最好的方法是使用shell脚本,因为在任何Linux系统中都应该有可用的shell。

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

  1. 我认为相同小写名称的文件应该被覆盖;这是用户的问题。当在不区分大小写的文件系统上检出时,它也会将前一个文件覆盖为后一个文件。

  2. 我会考虑A-Z字符并将它们转换为a-z,其他所有内容只会引发问题(至少对于源代码而言)。

  3. 脚本需要在Linux系统上运行构建,因此我认为应省略对CVS或Subversion版本控制文件的更改。毕竟,这只是一个临时检出。也许"export"更合适。


先前发布的内容对于简单情况可以直接使用或稍作调整即可完美运行,但在批量重命名之前,有一些情况需要考虑:1. 如果在路径层次结构中存在两个或多个仅大小写不同的名称(例如ABCdefabcDEFaBcDeF),应该发生什么? 重命名脚本是否应该中止或只是警告并继续执行?2. 如何为非美国ASCII名称定义小写字母?如果可能存在这样的名称,是否应首先进行检查和排除操作?3. 如果您正在运行重命名操作 - Mihai Limbășan
31个回答

2

详细而不冗长,“无惊喜,无需安装”

这个脚本可以处理带有空格、引号、其他特殊字符和Unicode的文件名,在大小写不敏感的文件系统和大多数已安装bash和awk的Unix-y环境中工作(即几乎所有环境)。如果存在任何冲突,它还会报告冲突(将文件名留在大写状态),当然也会递归地重命名文件和目录。最后它非常灵活:您可以调整查找命令以定位所需的文件/目录,并可以调整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中的小写?


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

)


1
如果您使用Arch Linux,您可以从AUR安装rename软件包,该软件包提供/usr/bin/renamexm可执行文件和手册页中的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文件,如果需要的话;)

1
在 macOS 上,将 brew 重命名时,-l 绑定到创建符号链接,-c 绑定到转换为小写字母,所以要小心。 - rien333

1
在这种情况下,我会选择使用Python,以避免过于乐观地假设路径不包含空格或斜杠。我也发现python2rename更普遍地安装在更多的位置。
#!/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])

谈论让它变得复杂...只需执行..重命名'y/A-Z/a-z/'* - Stephen
1
@Stephen,这假设您有管理员访问权限来安装重命名。我经常需要在我无法安装其他软件的系统上处理数据。在最近的Ubuntu中,只有在perl存在时才会安装它。 - John Foley

1

Slugify重命名(正则表达式)

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

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

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

2
“not exactly”? 这完全不是 OP 所问的。 - Corey Goldberg

1
我相信这个一行命令可以简化: for f in **/*; do mv "$f" "${f:l}"; done (说明:此命令可用于将当前目录及其子目录中的所有文件名转换为小写字母)

1

第一步

在MacOS上:

brew install rename

在Linux上:
sudo install rename

Second Step

find . -depth -execdir rename -f 'y/A-Z/a-z/' {} \;

解释: find . -depth:确保以深度优先的方式处理目录。在重命名父目录之前处理其子目录是很重要的,这样可以避免出现问题。
$ tree -L 3                  
.
└── ACB
    ├── HUS
    └── TEXT.AA

$ find .
.
./ACB
./ACB/TEXT.AA
./ACB/HUS

$ find . -depth                                      
./ACB/TEXT.AA
./ACB/HUS
./ACB
.

-execdir rename 'y/A-Z/a-z/' {} \;:使用Perl表达式'y/A-Z/a-z/'执行rename命令。 {}是目录名称的占位符,\;标记了-execdir选项的结束。

$ find . -depth -execdir rename -f 'y/A-Z/a-z/' {} \;
# success
$ find .
.
./acb
./acb/text.aa
./acb/hus

1

0

使用bash,不需要rename命令:

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

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

首先,自下而上地重命名目录sort -r(如果不存在-depth选项),然后是文件。 然后使用grep -v /CVS代替find ...-prune,因为它更简单。 对于大型目录,for f in ...可能会导致某些shell缓冲区溢出。 使用find ... | while read可以避免这种情况。
是的,这将破坏只在大小写方面不同的文件...

首先:“find YOURDIR -type d | sort -r” 太麻烦了。你需要使用 “find YOURDIR -depth -type d”。其次,在重命名目录之后必须运行“find -type f”。 - tzot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接