Как переименовать все папки и файлы в нижнем регистре в Linux?
Мне нужно переименовать полное дерево папок рекурсивно, чтобы нигде не было буквы верхнего регистра (это исходный код на С++, но это не имеет значения). Бонусные баллы за игнорирование файлов и папок CVS и SVN. Предпочтительным способом будет оболочка script, так как оболочка должна быть доступна в любом ящике Linux.
Были некоторые допустимые аргументы о деталях переименования файлов.
-
Я думаю, что файлы с такими же строчными именами должны быть перезаписаны, это проблема пользователя. При проверке на файловой системе, игнорирующей регистр, также будет перезаписываться первая с последней.
-
Я бы рассмотрел символы A-Z и преобразовал их в a-z, все остальное просто вызывает проблемы (по крайней мере, с исходным кодом).
-
Для запуска сборки в системе Linux понадобится script, поэтому я думаю, что изменения в управляющих файлах CVS или SVN следует опустить. В конце концов, это просто проверка на царапины. Может быть, "экспорт" более уместен.
Ответы
Ответ 1
Краткая версия с использованием команды "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
Р. S.
Последний позволяет большую гибкость с командой перемещения (например, "svn mv"
).
Ответ 2
меньше, мне все равно нравится
rename 'y/A-Z/a-z/' *
В нечувствительных к регистру файловых системах, таких как OS X HFS +, вы хотите добавить флаг -f
rename -f 'y/A-Z/a-z/' *
Ответ 3
for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
Ответ 4
Просто попробуйте следовать, если вам не нужно заботиться об эффективности.
zip -r foo.zip foo/*
unzip -LL foo.zip
Ответ 5
Большинство вышеперечисленных ответов опасны, поскольку они не занимаются именами, содержащими нечетные символы. Ваша самая безопасная ставка для такого рода вещей заключается в использовании опции find -print0, которая прекратит имена файлов с помощью ascii NUL вместо \n. Здесь я отправляю этот script, который только изменяет файлы, а не имена каталогов, чтобы не путать 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, один из этих других script на дереве, который содержит файл, созданный:
touch \;\ echo\ hacker::0:0:hacker:\$\'\057\'root:\$\'\057\'bin\$\'\057\'bash
... хорошо угадайте, что...
Ответ 6
Это работает, если у вас уже есть или настроена команда переименования (например, через brew install в Mac):
rename --lower-case --force somedir/*
Ответ 7
Человек, которого вы, ребята, усложняете.
переименовать 'y/A-Z/a-z/' *
Ответ 8
Это работает с CentOS/Redhat или другими дистрибутивами без rename
Perl script:
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
(в некоторых дистрибутивах команда по умолчанию rename
поступает из util-linux, и это другой, несовместимый инструмент)
Ответ 9
Здесь мое субоптимальное решение, используя bash Shell script:
#!/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" для поиска папок.
Ответ 10
Использование фиксатора имени файла Larry Wall
$op = shift or die $help;
chomp(@ARGV = <STDIN>) unless @ARGV;
for (@ARGV) {
$was = $_;
eval $op;
die [email protected] if [email protected];
rename($was,$_) unless $was eq $_;
}
это так же просто, как
find | fix 'tr/A-Z/a-z/'
(где fix - это, конечно, script выше)
Ответ 11
Исходный вопрос просил игнорировать каталоги SVN и CVS, что можно сделать, добавив -prune в команду find. Например, чтобы игнорировать 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 '{}' \;
Ян
Ответ 12
Это небольшая оболочка script, которая делает то, что вы просили:
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 в первом поиске.
Ответ 13
Ранее опубликованные будут работать совершенно из коробки или с некоторыми настройками для простых случаев, но есть некоторые ситуации, которые вы, возможно, захотите принять во внимание перед запуском переименования пакетов:
-
Что произойдет, если у вас есть два или более имен на одном уровне иерархии путей, которые различаются только в зависимости от случая, например ABCdef
, ABCdef
и ABCdef
? Должно ли переименовать script прервать или просто предупредить и продолжить?
-
Как вы определяете строчные буквы для имен без US-ASCII? Если такие имена могут присутствовать, следует ли сначала выполнить проверку и исключить проход?
-
Если вы выполняете операцию переименования в рабочих копиях CVS или SVN, вы можете повредить рабочую копию, если вы измените регистр на имена файлов или каталогов. Должно ли script находить и настраивать внутренние административные файлы, такие как .svn/entries или CVS/Entries?
Ответ 14
С 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
Не переносимый, только Zsh, но довольно сжатый.
Сначала убедитесь, что загружен zmv
.
autoload -U zmv
Кроме того, убедитесь, что extendedglob
включен:
setopt extendedglob
Затем используйте:
zmv '(**/)(*)~CVS~**/CVS' '${1}${(L)2}'
Рекурсивно строчные файлы и каталоги, где имя не CVS.
Ответ 16
В 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
В этой ситуации я мог бы использовать 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
for f in `find -depth`; do mv ${f} ${f,,} ; done
find -depth
печатает каждый файл и каталог с содержимым каталога, напечатанным перед самим каталогом. ${f,,}
уменьшает имя файла.
Ответ 19
( 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... -рубят, потому что это проще.
Для больших каталогов для f в... может переполнять некоторые оболочки оболочки.
Используйте поиск... | читайте, чтобы этого не было.
И да, это будут clobber файлы, которые отличаются только случаем...
Ответ 20
Мне нужно было сделать это на установке cygwin в Windows 7 и обнаружил, что у меня возникли синтаксические ошибки с предложениями выше, которые я пытался (хотя я, возможно, пропустил рабочий параметр), однако это решение прямо из форумы ubutu отработал из can: -)
ls | while read upName; do loName=`echo "${upName}" | tr '[:upper:]' '[:lower:]'`; mv "$upName" "$loName"; done
(nb Я раньше заменял пробелы символами подчеркивания, используя
for f in *\ *; do mv "$f" "${f// /_}"; done
Ответ 21
Slugify Переименовать (regex)
Не совсем то, что запросил OP, но то, что я надеялся найти на этой странице:
Версия "slugify" для переименования файлов, так что они похожи на URL-адреса
(т.е. включают только буквенно-цифровые, точки и тире):
rename "s/[^a-zA-Z0-9\.]+/-/g" filename
Ответ 22
Если вы используете Arch Linux, вы можете установить rename
из AUR
, который предоставляет команду renamexm
как /usr/bin/renamexm
исполняемый файл и manual вместе с ним.
Это действительно мощный инструмент для быстрого переименования файлов и каталогов.
Преобразовать в нижний регистр
rename -l Developers.mp3 # or --lowcase
Преобразовать в корпус UPPER
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
Вы можете получить образец файла Developers.mp3 из здесь, если это необходимо;)
Ответ 23
Никто не предлагает набор символов?
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
В эмуляциях Windows, таких как git bash, это может завершиться неудачно, потому что окна не чувствительны к регистру под капотом. Для этого добавьте шаг, в котором сначала будет файл с другим именем, например "$ old.tmp", а затем в $new.
Ответ 24
Длительный, но "Работает без сюрпризов и без установок"
Этот скрипт обрабатывает имена файлов с пробелами, кавычками, другими необычными символами и Unicode, работает с нечувствительными к регистру файловыми системами и большинством сред Unix-y, в которых установлены bash и awk (т.е. почти все). Он также сообщает о коллизиях, если таковые имеются (оставляя имя файла в верхнем регистре), и, конечно, переименовывает файлы и каталоги и работает рекурсивно. Наконец, он легко адаптируется: вы можете настроить команду 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?
Ответ 25
Это хорошо работает и в macOS:
ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"
Ответ 26
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
интерпретатору оболочки. Вы можете заменить его на bash
или любую другую оболочку по своему вкусу.
Ответ 27
Простейший подход, который я нашел в MacOSX, заключался в использовании пакета переименования с http://plasmasturm.org/code/rename/:
brew install rename
rename --force --lower-case --nows *
--force Переименовывайте, даже если файл с именем получателя уже существует.
--lower-case Конвертировать имена файлов в нижний регистр.
--nows Заменить все последовательности пробелов в имени файла символами подчеркивания.