Как переименовать все папки и файлы в нижний регистр в Linux?

179

Я должен рекурсивно переименовать полное дерево папок, чтобы нигде не было заглавных букв (это исходный код C ++, но это не должно иметь значения).

Бонусные баллы за игнорирование файлов / папок контроля версий CVS и Subversion. Предпочтительным способом будет сценарий оболочки, поскольку оболочка должна быть доступна на любом компьютере с Linux.

Были некоторые веские аргументы о деталях переименования файла.

  1. Я думаю, что файлы с одинаковыми строчными именами должны быть перезаписаны; это проблема пользователя. При проверке в файловой системе без учета регистра она также перезаписывает первую на последнюю.

  2. Я хотел бы рассмотреть символы AZ и преобразовать их в az, все остальное просто вызывает проблемы (по крайней мере, с исходным кодом).

  3. Сценарий понадобится для запуска сборки в системе Linux, поэтому я думаю, что изменения в файлах управления версиями CVS или Subversion должны быть опущены. В конце концов, это просто проверка заказа. Может быть, «экспорт» более уместен.

vividos
источник
Ранее опубликованные данные будут отлично работать "из коробки" или с некоторыми изменениями для простых случаев, но есть некоторые ситуации, которые вы можете принять во внимание перед запуском пакетного переименования: 1. Что должно произойти, если у вас есть два или более имен в один и тот же уровень в иерархии путей, которые отличаются только регистром, например ABCdef, abcDEFи aBcDeF? Должен ли сценарий переименования прерваться или просто предупредить и продолжить? 2. Как вы определяете нижний регистр для не-US-ASCII имен? Если такие имена могут присутствовать, следует ли сначала выполнить проверку и исключить проход? 3. Если вы выполняете операцию переименования
Mihai Limbășan

Ответы:

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

PS

Последнее обеспечивает большую гибкость с помощью команды перемещения (например, "svn mv").

Алекс Б
источник
36
использовать переименование можно так же, как переименование 'y / AZ / az /' *
Цуры Бар Йохай
3
Осторожно, обе версии не будут работать в нечувствительной к регистру файловой системе. В моем случае я заменил- thenзакрытую строку на (mv "${SRC}" "${DST}.renametmp" && mv "${DST}.renametmp" "${DST}") || echo "${SRC} was not renamed".
Lloeki
6
Второй подход не работал правильно для меня с файлами, содержащими пробелы (контекст: linux, bash).
Дим
4
Используя последнюю версию переименования, регулярное выражение не требуется. Полная команда становится find my_root_dir -depth -exec rename -c {} \;. Добавьте -f для переименования, если вы используете файловую систему без
учета
Второй сработал для меня в Solaris, удалив команду -Tиз mvкоманды.
Ветчина
262

Меньше все же мне очень нравится

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

В нечувствительных к регистру файловых системах, таких как HFS + в OS X , вы захотите добавить -fфлаг:

rename -f 'y/A-Z/a-z/' *
tristanbailey
источник
3
linux.icydog.net/rename.php :The renaming utility that comes by default with Ubuntu is a Perl program sometimes called prename
Sleepsort
1
Спасибо, я использовал это таким образом $ find | xargs переименовывает 'y / AZ / az /' *
Раши
5
Это предполагает, что у вас есть perls rename, что не всегда так. например на моем debian переименование совершенно другое
Кшиштоф Красонь
10
На Arch программа называется perl-rename(и предоставляется пакетом с таким же именем)
jaymmer - Reinstate Monica
3
Это не решает данную проблему, так как «переименование» не является рекурсивным.
Cybolic
89
for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
Swaroop CH
источник
Пожалуйста, сделайте «mkdir -p A / B / C» перед запуском вашего скрипта.
Цот
2
«find» следует заменить любой командой, которую вы используете, чтобы получить список файлов, которые вы хотите переименовать; например, "ls *. {C, CPP, H}".
JPaget
2
Не работает на OS X, просто печатает информацию об использовании find. find .кажется, это исправить.
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
Ginhing
источник
13
Вау, это довольно неэффективный способ.
Artjom B.
28
Предполагая, что вычислительная эффективность обычно НЕ является проблемой, когда вам нужно переименовать файлы в каталоге ... Я думаю, что это, вероятно, самое творчески простое решение в этой теме. На самом деле, эта тривиальная задача не должна быть такой сложной, как демонстрируют принятые ответы, и этот обходной путь гораздо более пропорционален тому, сколько мыслей я хочу инвестировать в такую ​​операцию, как эта. +1000
Питер М. Элиас
27
Вы можете ускорить его с помощью -0переключателя (это «ноль», без сжатия) zip -0 -r foo.zip foo/.
Джонни Балони
3
Мне пришлось изменить более 50 000 имен файлов в разных структурах каталогов. Первоначально принятый ответ выполнил лишь 10% работы за 2 минуты, где это, со -0сжатием, было почти мгновенным!
Peon
2
Это не просто неэффективно. Это просто невозможно, если для временного архива недостаточно свободного места.
Виктор Ярема
20

Чувак, вы, ребята, любите усложнять вещи ... Использование:

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, т.е. попробуйте переименовать весь путь, а не только файл.
Пабло А
14

Большинство ответов выше опасны, потому что они не имеют дело с именами, содержащими нечетные символы. Ваш безопасный выбор для такого рода вещи , чтобы использовать find«s -print0вариант, который будет прекратить имена файлов с ASCII NUL вместо \ п.

Вот скрипт, который изменяет только файлы, а не имена каталогов, чтобы не перепутать 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

... ну угадай что ...

niXar
источник
Было бы проще использовать renameвместо mv.
Виктор Ярема
14

Это работает, если у вас уже есть или настроена команда переименования (например, через brew install в Mac):

rename --lower-case --force somedir/*
alemol
источник
9
... если вы сначала настроите переименование, например, черезbrew install rename
pduersteler
3
переименуйте 'y / AZ / az /' *
Стивен
4
--lower-case? К сожалению, в Arch Linux это не так.
3
renameэто не встроенный bash. Это внешняя утилита, и ее синтаксис зависит от версии, которую вы установили. Этот ответ не является достаточным в качестве портативного решения "для всех ОС на основе Unix", как утверждается.
Кори Голдберг
2
Только что проверил, renameкоманда, включенная в последнюю Fedora (28) через util-linux-2.31пакет, не имеет --lower-caseопции.
niXar
12

Это работает на 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, а это другой несовместимый инструмент.)

Pere
источник
Это работает только для одного каталога, а не рекурсивно
Брэм
Coool .. Работает хорошо
Manikandan Ram
6

Самым простым подходом, который я нашел в Mac OS X, было использование пакета переименования с http://plasmasturm.org/code/rename/ :

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

--force Переименовать, даже если файл с именем назначения уже существует.

--lower-case Конвертировать имена файлов в нижний регистр.

--nows Заменить все последовательности пробелов в имени файла символами подчеркивания.

Ким Т
источник
5

Вот мое неоптимальное решение с использованием сценария оболочки Bash:

#!/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» для поиска папок.

vividos
источник
Ваша первая находка не работает. Попробуйте «mkdir -p A / B / C» и затем запустите ваш скрипт.
Цот
4

Первоначальный вопрос касался игнорирования каталогов 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 '{}'  \;

Ян

Йен Дикинсон
источник
4

Используя средство для исправления имени Ларри Уолла:

$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конечно сценарий выше)

agnul
источник
3

Это небольшой сценарий оболочки, который делает то, что вы просили:

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 в первой находке.

tzot
источник
1
Единственное, что работало на Mac из целого потока. Спасибо, куча!
YemSalat
2

Не портативный, только Zsh, но довольно лаконичный.

Сначала убедитесь, что zmvзагружен.

autoload -U zmv

Также убедитесь, что extendedglob:

setopt extendedglob

Тогда используйте:

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

Для рекурсивных строчных файлов и каталогов, где имя не CVS .

benjwadams
источник
2
for f in `find -depth`; do mv ${f} ${f,,} ; done

find -depthпечатает каждый файл и каталог с содержимым каталога, напечатанным перед самим каталогом. ${f,,}строчные буквы имени файла.

Крис Ширколк
источник
Это не работает: он переименовывает каталог до того, как работает с содержимым, так что последующая попытка содержимого не удалась.
Чернослив
Добавление -depthисправляет это! Это действительно быстрое решение, но, конечно, без -print0возможности найти, оно не самое надежное
jpaugh
@jpaugh Нет, это не так. Он попытается переименовать ./FOO/BAR в ./foo/bar, но ./foo еще не существует.
oisyn
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здесь?
Виктор Ярема
- IФлаг не является обязательным. Он используется, когда мы выполняем несколько команд с xargs. Ниже приведен пример использования нескольких команд с xargs cat foo.txt | xargs -I % sh -c 'echo %; mkdir %' Вами может работать без -Iкакfind . -iname "*.py" -type f | xargs -I% rename -c -f "%"
akilesh Радж
2

Используйте typeset :

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, работает с нечувствительными к регистру файловыми системами и большинством сред 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 {} +

Ссылки

Мой сценарий основан на этих превосходных ответах:

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

Как преобразовать строку в нижний регистр в Bash?

ndemou
источник
1

Это хорошо работает и в macOS:

ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"
Крис
источник
Это особенно приятно, поскольку Ruby поставляется с macOS.
jxmorris12
1

Мне нужно было сделать это на установке Cygwin в Windows 7 и обнаружил, что я получил синтаксические ошибки с предложениями из вышеупомянутого, которые я попробовал (хотя, возможно, я пропустил рабочий вариант). Однако, это решение прямо с форумов Ubuntu сработало из банки :-)

ls | while read upName; do loName=`echo "${upName}" | tr '[:upper:]' '[:lower:]'`; mv "$upName" "$loName"; done

(Примечание: ранее я заменил пробел подчеркиванием, используя:

for f in *\ *; do mv "$f" "${f// /_}"; done

)

jacanterbury
источник
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 /' *
Стивен
1
@Stephen, который предполагает, что у вас есть права администратора для установки переименования. Мне часто приходится извлекать данные в системах, на которых у меня нет возможности устанавливать другое программное обеспечение. В последних версиях Ubuntus он устанавливается только при наличии perl.
Джон Фоли
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
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, потому что это проще. Для больших каталогов, если f in ... может переполнить некоторые буферы оболочки. Используйте поиск ... | в то время как читать, чтобы избежать этого.

И да, это будет забивать файлы, которые отличаются только в случае ...

pklausner
источник
Во-первых, find YOURDIR -type d | sort -rэто слишком много проблем. Вы хотите find YOURDIR -depth -type d. Во-вторых, find -type fДОЛЖЕН выполняться после того, как каталоги были переименованы.
Цот
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команды в интерпретатор оболочки. Вы можете заменить его bashили любую оболочку по своему вкусу.

oisyn
источник
0

Slugify Rename (регулярное выражение)

Это не совсем то, о чем просил ФП, но то, что я надеялся найти на этой странице:

Версия «slugify» для переименования файлов, чтобы они были похожи на URL-адреса (т. Е. Включают только буквенно-цифровые, точки и тире):

rename "s/[^a-zA-Z0-9\.]+/-/g" filename
УХО
источник
2
"не совсем"? это совсем не то, о чем просил ОП
Кори Голдберг