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


179

我必须递归重命名完整的文件夹树,以便任何地方都没有大写字母(这是C ++源代码,但这没关系)。

忽略CVS和Subversion版本控制文件/文件夹的加分点。首选方法是shell脚本,因为shell应该在任何Linux机器上都可用。

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

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

  2. 我会考虑AZ字符并将其转换为az,其他所有内容都只是在引起问题(至少在源代码中)。

  3. 在Linux系统上运行构建将需要该脚本,因此我认为应省略对CVS或Subversion版本控制文件的更改。毕竟,这只是从头开始。也许“出口”更合适。


以前发布的内容可以在开箱即用的情况下很好地工作,或者在一些简单的情况下可以进行一些调整,但是在某些情况下,您可能需要在运行批处理重命名之前加以考虑:1.如果您有两个或多个名称,该怎么办相同的水平的路径分级其中只有大小写不同,例如ABCdefabcDEFaBcDeF?重命名脚本应该中止还是只是警告并继续?2.如何为非US-ASCII名称定义小写?如果可能出现这样的名称,是否应该首先执行一次检查并排除通过?3.如果您正在运行重命名操作
Mihai Limbășan

Answers:


179

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

聚苯乙烯

后者使move命令(例如"svn mv")具有更大的灵活性。


36
使用重命名也可以通过这种方式完成,也可以重命名'y / AZ / az /'*
Tzury Bar Yochay 09年

3
请注意,这两个版本均不适用于不区分大小写的文件系统。在我的情况下,我将- then括起来的行替换为(mv "${SRC}" "${DST}.renametmp" && mv "${DST}.renametmp" "${DST}") || echo "${SRC} was not renamed"
Lloeki 2011年

6
第二种方法不适用于包含空格的文件(上下文:linux,bash)。
暗淡

4
使用重命名的最新版本,不需要正则表达式。完整的命令变为find my_root_dir -depth -exec rename -c {} \;。如果您使用的是不区分大小写的文件系统(例如Mac),请添加-f重命名
Javache 2011年

第二个通过消除为我工作在Solaris上-Tmv命令。
火腿

261

较小的我还是很喜欢:

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

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

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

3
linux.icydog.net/rename.phpThe renaming utility that comes by default with Ubuntu is a Perl program sometimes called prename
sleepsort

1
谢谢,我用这种方式。xargs重命名“ y / AZ / az /” *
拉希

5
这假设您具有perls重命名,但情况并非总是如此。例如,在我的debian重命名上完全不同
KrzysztofKrasoń15年

10
在Arch上,该程序被调用perl-rename(并由具有相同名称的软件包提供)
jaymmer-恢复莫妮卡(Monica

3
这不能解决给定的问题,因为“重命名”不是递归的。
Cybolic

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

在运行脚本之前,请先执行“ mkdir -p A / B / C”。
tzot

2
应该使用您用来获取要重命名的文件列表的任何命令来替换“查找”。例如“ ls *。{C,CPP,H}”。
JPaget 2014年

2
在OS X上不起作用,仅显示的使用情况信息findfind .似乎解决了这个问题。
emlai

3
来自所有注释的“合并”版本(不再可以编辑答案):for f in `find .`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
romaricdrigon

1
有人可以向我解释为什么这首先应该起作用吗?如果找到./FOO和./FOO/BAR,则首先将./FOO重命名为./foo,此后将不再找到./FOO/BAR。在下面输入我自己的答案应该可以解决此问题。
oisyn

80

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

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

13
哇,那是一种非常低效的方式。
Artjom B. 2014年

28
假设您发现自己需要重命名目录中的文件时,通常无需考虑计算效率...我认为这可能是该线程上最富创意的简单解决方案。确实,这种琐碎的事情不应该像已接受的答案所显示的那样复杂,并且这种解决方法与我想投资于此类行动的思想量更为相称。+1000
彼得·埃里亚斯

27
您可以使用-0(即“零”,不压缩)开关加快速度zip -0 -r foo.zip foo/
约翰尼·布洛尼

3
我不得不在不同的目录结构中更改超过50'000个文件名。最初接受的答案在2分钟内仅完成了10%的工作,而-0压缩的结果几乎是即时的!
Peon'2

2
这不仅效率低下。如果没有足够的可用空间来存储临时存档,那么这根本是不可能的
Victor Yarema '18

18

伙计们,你们喜欢过分复杂的事情...使用:

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

2
这不起作用,它说名称小写的文件已经存在。
kirhgoff

@kirhgoff我已经看到NFS挂载会发生这种情况-请按照此处的说明进行操作。
joebeeson

我们如何在区分大小写的文件系统上重命名?rename 'y/a-z/A-Z/' /tmp/*.jpg-> Can't rename /tmp/*.jpg /TMP/*.JPG: No such file or directory,即,尝试重命名整个路径而不是仅重命名文件。
Pablo A

14

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

...猜猜是什么...


rename代替使用会更简单mv
Victor Yarema '18

14

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

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

9
...如果您先设置重命名,例如通过brew install rename
pduersteler

3
重命名'y / AZ / az /'*
Stephen

4
--lower-case?不幸的是,至少在Arch Linux中并非如此。

3
rename不是内置的bash。它是一个外部实用程序,语法会根据您安装的版本而有所不同。如所要求的,该答案不足以作为“针对所有基于unix的OS”的便携式解决方案。
科里·戈德堡

2
刚刚选中,rename最新的Fedora(28)中包含的命令(通过util-linux-2.31软件包)没有该--lower-case选项。
niXar


6

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

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

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

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

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


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”搜索文件夹。


您的第一个发现不起作用。尝试“ mkdir -p A / B / C”,然后运行脚本。
tzot

4

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

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

[edit]我尝试了一下,由于我实际上不了解的原因,将小写翻译嵌入到搜索结果中无法正常工作。因此,将其修改为:

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

伊恩


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


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操作。


1
整个线程在Mac上唯一起作用的东西。谢谢堆!
YemSalat

2

不便携,仅Zsh,但非常简洁。

首先,请确保zmv已加载。

autoload -U zmv

另外,请确保extendedglob已打开:

setopt extendedglob

然后使用:

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

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


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

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


这是行不通的:在对内容进行操作之前,它会重命名目录,这样以后对内容的尝试将失败。
修剪

添加-depth修复程序!这是一个非常快速的解决方案,但是当然,如​​果不使用-print0查找选项,它就不是最可靠的
jpaugh

@jpaugh不,不是。它将尝试将./FOO/BAR重命名为./foo/bar,但当时尚不存在./foo。
oisyn19年

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

为什么在这里使用-I选项xargs
Victor Yarema '18

- I标志不是必需的。当我们使用执行多个命令时使用它xargs。这是一个与xargs cat foo.txt | xargs -I % sh -c 'echo %; mkdir %' 您一起使用多个命令的示例,您可以运行时-I不像find . -iname "*.py" -type f | xargs -I% rename -c -f "%"
akilesh raj

2

使用排版

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


1

漫长而又“没有惊奇,没有装置的作品”

该脚本处理带有空格,引号,其他不寻常字符和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 {} +

参考资料

我的脚本基于以下出色的答案:

/unix/9496/looping-through-files-with-spaces-in-the-names

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


1

这在macOS上也很好用:

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

这是特别好的,因为Ruby随macOS一起提供。
jxmorris12

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

在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

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

谈论使其变得复杂...就做..重命名'y / AZ / az /'*
Stephen

1
@Stephen,假设您具有安装重命名的管理员权限。我经常需要在我无法安装其他软件的系统上整理数据。在最近的Ubuntu中,仅当存在perl时才安装。
John Foley'17

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文件;)


1
brew在macOS上重命名-l必须创建一个符号链接,并-c转换为小写字母,所以要当心。
rien333 '17

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,因为它更简单。对于大目录,对于f in ...可能会溢出一些shell缓冲区。使用查找... | 在阅读时以避免这种情况。

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


第一:find YOURDIR -type d | sort -r太麻烦了。你要find YOURDIR -depth -type d。其次,find -type f必须在目录重命名后运行。
tzot

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'

此部分读取输出的文件,findmv使用正则表达式在命令中格式化它们。该-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或任何您喜欢的外壳。


0

Slugify重命名(regex)

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

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

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

2
“不完全是”?这是不是在所有什么OP要求
科里·戈德堡
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.