[英]How do I rename all folders and files to lowercase on Linux?
我必須遞歸地重命名一個完整的文件夾樹,這樣任何地方都不會出現大寫字母(它是 C++ 源代碼,但這無關緊要)。
忽略 CVS 和 Subversion 版本控制文件/文件夾的加分項。 首選方式是 shell 腳本,因為 shell 應該在任何 Linux 盒子上可用。
關於文件重命名的詳細信息,有一些有效的 arguments。
我認為應該覆蓋具有相同小寫名稱的文件; 這是用戶的問題。 當在忽略大小寫的文件系統上檢出時,它也會用后者覆蓋第一個。
我會考慮 AZ 字符並將它們轉換為 az,其他一切都只是在尋找問題(至少對於源代碼而言)。
在 Linux 系統上運行構建需要該腳本,所以我認為應該省略對 CVS 或 Subversion 版本控制文件的更改。 畢竟,這只是一個刮擦結帳。 也許“出口”更合適。
使用"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"
)。
for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
如果您不需要關心效率,只需簡單地嘗試以下操作。
zip -r foo.zip foo/*
unzip -LL foo.zip
伙計,你們/女孩喜歡把事情復雜化……使用:
rename 'y/A-Z/a-z/' *
這適用於CentOS / Red Hat Linux或其他發行版,無需rename
Perl 腳本:
for i in $( ls | grep [A-Z] ); do mv -i "$i" "`echo $i | tr 'A-Z' 'a-z'`"; done
(在某些發行版中,默認的rename
命令來自 util-linux,這是一個不同的、不兼容的工具。)
上面的大多數答案都很危險,因為它們不處理包含奇數字符的名稱。 對於這種事情,您最安全的選擇是使用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
......好吧你猜怎么着......
如果您已經擁有或設置了重命名命令(例如通過 Mac 中的brew install ),這將起作用:
rename --lower-case --force somedir/*
我在 Mac OS X 上找到的最簡單的方法是使用來自http://plasmasturm.org/code/rename/的重命名包:
brew install rename
rename --force --lower-case --nows *
--force 重命名即使具有目標名稱的文件已經存在。
--lower-case 將文件名全部轉換為小寫。
--nows 用單個下划線字符替換文件名中的所有空格序列。
這是我使用 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”來搜索文件夾。
使用 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
當然是上面的腳本)
最初的問題要求忽略 SVN 和 CVS 目錄,這可以通過將 -prune 添加到 find 命令來完成。 例如忽略 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 '{}' \;
伊恩
這是一個執行您要求的小型 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 操作。
不便攜,僅限 Zsh,但非常簡潔。
首先,確保加載了zmv
。
autoload -U zmv
此外,確保extendedglob
已打開:
setopt extendedglob
然后使用:
zmv '(**/)(*)~CVS~**/CVS' '${1}${(L)2}'
遞歸小寫名稱不是CVS 的文件和目錄。
之前發布的內容可以完美地開箱即用,或者針對簡單案例進行一些調整,但在運行批量重命名之前,您可能需要考慮一些情況:
如果在路徑層次結構中同一級別有兩個或多個名稱,只有大小寫不同,例如ABCdef
, abcDEF
和aBcDeF
, abcDEF
aBcDeF
什么? 重命名腳本應該中止還是只是警告並繼續?
如何為非US-ASCII名稱定義小寫? 如果可能存在此類名稱,是否應首先執行檢查和排除傳遞?
如果在CVS或SVN工作副本上運行重命名操作,則更改文件或目錄名稱的大小寫可能會損壞工作副本。 該腳本是否還應查找並調整內部管理文件,如.svn / entries或CVS / Entries?
在 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
for f in `find -depth`; do mv ${f} ${f,,} ; done
find -depth
打印每個文件和目錄,並在目錄本身之前打印目錄的內容。 ${f,,}
小寫文件名。
使用排版:
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
。
使用 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
冗長但“沒有驚喜,沒有安裝”
該腳本處理帶有空格、引號、其他不尋常字符和 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
這也適用於 macOS:
ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"
我需要在 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
)
這不完全是 OP 所要求的,而是我希望在此頁面上找到的內容:
用於重命名文件的“slugify”版本,使它們類似於 URL(即僅包含字母數字、點和破折號):
rename "s/[^a-zA-Z0-9\.]+/-/g" filename
在這種情況下,我會使用 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])
如果你使用Arch Linux ,你可以從AUR
安裝rename
) 包,它提供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文件;)
單線:
for F in K*; do NEWNAME=$(echo "$F" | tr '[:upper:]' '[:lower:]'); mv "$F" "$NEWNAME"; done
請注意,這將僅轉換以字母 K 開頭的文件/目錄,因此請進行相應調整。
我相信單線可以簡化:
for f in **/*; do mv "$f" "${f:l}"; done
( 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 . -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/BAR
重命名為./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
。 mv
的-n
選項確保現有的小寫文件不會被覆蓋。 -v
選項使mv
輸出它所做的每個更改(或不進行更改 - 如果./FOO/bar
已經存在,它會輸出類似./FOO/BAR -> ./FOO/BAR
,注意沒有更改制作)。 -T
在這里非常重要 - 它將目標文件視為一個目錄。 如果該目錄恰好存在,這將確保./FOO/BAR
不會移動到./FOO/bar
。
將此與find
一起使用以生成將要執行的命令列表(方便驗證無需實際執行即可完成的操作)
sh
這不言自明。 它將所有生成的mv
命令路由到 shell 解釋器。 您可以用bash
或您喜歡的任何 shell 替換它。
這里沒有一個解決方案對我有用,因為我在一個無法訪問 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 上類似問題“將整個目錄樹更改為小寫名稱”的答案。
使用 bash,不rename
:
find . -exec bash -c 'mv $0 ${0,,}' {} \;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.