Как переименовать все папки и файлы в нижнем регистре в 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 Заменить все последовательности пробелов в имени файла символами подчеркивания.