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

StackOverflow https://stackoverflow.com/questions/152514

  •  02-07-2019
  •  | 
  •  

Вопрос

Мне приходится рекурсивно переименовывать все дерево папок, чтобы нигде не появлялась заглавная буква (это исходный код C++, но это не имеет значения).Бонусные баллы за игнорирование управляющих файлов/папок CVS и SVN.Предпочтительным способом будет сценарий оболочки, поскольку оболочка должна быть доступна в любом компьютере Linux.

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

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

  2. Я бы рассмотрел символы от A до Z и преобразовал их в a-z, все остальное просто вызывает проблемы (по крайней мере, с исходным кодом).

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

Это было полезно?

Решение

Краткая версия с использованием "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").

Другие советы

еще меньше, мне очень нравится

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

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

rename -f 'y/A-Z/a-z/' *
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

Большинство приведенных выше ответов опасны, поскольку они не касаются имен, содержащих нечетные символы.Самый безопасный вариант для такого рода вещей — использовать опцию find -print0, которая будет завершать имена файлов ascii NUL вместо .Здесь я представляю этот скрипт, который изменяет только файлы, а не имена каталогов, чтобы не путать поиск.

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

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

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

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

Ребята, вы любите все усложнять..

переименовать 'y/A-Z/a-z/' *

Это работает в CentOS/Redhat или других дистрибутивах без rename Перл-скрипт:

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

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

$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/'

(где исправление - это, конечно, скрипт выше)

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

Ян

Вот мое неоптимальное решение с использованием сценария оболочки 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 -глубина» и «найти | сортировка -r» возвращают список папок в удобном для переименования порядке, я предпочел использовать «-глубину» для поиска папок.

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

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

Обратите внимание на действие -глубина в первой находке.

Ранее опубликованный вариант будет отлично работать «из коробки» или с некоторыми корректировками для простых случаев, но есть некоторые ситуации, которые вы, возможно, захотите принять во внимание, прежде чем запускать пакетное переименование:

  1. Что должно произойти, если у вас есть два или более имен на одном уровне иерархии путей, которые различаются только регистром, например: ABCdef, abcDEF и aBcDeF?Должен ли сценарий переименования прерваться или просто предупредить и продолжить?

  2. Как определить нижний регистр для имен, отличных от US-ASCII?Если такие имена могут присутствовать, следует ли сначала выполнить проверку и исключение?

  3. Если вы выполняете операцию переименования рабочих копий CVS или SVN, вы можете повредить рабочую копию, если измените регистр в именах файлов или каталогов.Должен ли сценарий также найти и настроить внутренние административные файлы, такие как .svn/entries или CVS/Entries?

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

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

С 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

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

Во-первых, убедитесь, zmv загружен.

autoload -U zmv

Также убедитесь, extendedglob включен:

setopt extendedglob

Затем используйте:

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

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

В 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

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

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

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

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

В этой ситуации я бы обратился к 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])

Длинно, но «Работает без сюрпризов и никаких установок»

Этот сценарий обрабатывает имена файлов с пробелами, кавычками и прочим необычный символов и Unicode, работает в файловых системах, нечувствительных к регистру, и в большинстве Unix-сред. с установленными 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?

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

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

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

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

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

( 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 (где параметр -глубина недоступен), затем файлы.Затем grep -v/CVS вместо найти...-обрезать потому что это проще.Для больших каталогов для ж в... может переполнить некоторые буферы оболочки.Использовать находить ...| во время чтения чтобы избежать этого.

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

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

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

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

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

Если вы используете Арч Линукс, вы можете установить 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 # substite string on pattern

--yes # Confirm all actions

Никто не предлагает верстать?

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, это может привести к сбою, поскольку Windows не чувствительна к регистру.Для этого добавьте шаг, который сначала присваивает файлу другое имя, например «$old.tmp», а затем — $new.

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

ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"
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 или любая оболочка по вашему вкусу.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top