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


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

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

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

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

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

24 140

24 ответа:

краткий вариант с использованием .

find my_root_dir -depth -exec rename 's/(.*)\/([^\/]*)/\/\L/' {} \;

это позволяет избежать проблем с переименованием каталогов перед файлами и попыткой переместить файлы в несуществующие каталоги (например,"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/' *

в нечувствительных к регистру файловых системах, таких как OS X HFS+, вы захотите добавить флаг-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 вместо \n. здесь я представляю этот скрипт, который только изменяет файлы, а не имена каталогов, чтобы не путать find.

find .  -type f -print0 | xargs -0n 1 bash -c \
's=$(dirname "")/$(basename ""); 
d=$(dirname "")/$(basename ""|tr "[A-Z]" "[a-z]"); mv -f "$s" "$d"'

Я тестировал его, и он работает с именами файлов, содержащими пробелы, все виды кавычек и т. д. Это важно, потому что если вы запустите, как root, один из тех других скриптов в дереве, который включает файл, созданный:

touch \;\ echo\ hacker::0:0:hacker:$\'7\'root:$\'7\'bin$\'7\'bash

... Ну знаешь что ...

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

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

человек, вы, ребята, любите усложнять вещи..

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

С помощью Larry Wall's filename fixer

$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

[edit] я попробовал это, и внедрение перевода нижнего регистра внутри находки не сработало по причинам, которые я на самом деле не понимаю. Итак, измените это следующим образом:

$> cat > tolower
#!/bin/bash
mv  `echo  | 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

Edit: я сделал некоторые изменения, основанные на предложениях до сих пор. Теперь папки все переименованы правильно, mv не задает вопросы, когда разрешения не совпадают, и папки CVS не переименованы (файлы управления CVS внутри этой папки все еще переименованы, к сожалению).

Edit: поскольку "find-depth" и "find | sort-r" оба возвращают список папок в удобном порядке для переименования, я предпочел использование "- глубина " для поиска папок.

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

root_directory="${1?-please specify parent directory}"
do_it () {
    awk '{ lc= tolower(); if (lc != ) print "mv \""   "\" \"" lc "\"" }' | sh
}
# first the folders
find "$root_directory" -depth -type d | do_it
find "$root_directory" ! -type d | do_it

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

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

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

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

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

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

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

С MacOS,

установить rename пакет,

brew install rename

использовать

find . -iname "*.py" -type f | xargs -I% rename -c -f  "%"                       

эта команда находит все файлы с

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

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

autoload -U zmv

кроме того, убедитесь, что extendedglob на:

setopt extendedglob

затем использовать:

zmv '(**/)(*)~CVS~**/CVS' '${(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

Я бы потянулся к 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])
for f in `find -depth`; do mv ${f} ${f,,} ; done

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

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

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

Slugify переименовать (регулярное выражение)

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

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

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

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

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

(nb я ранее заменял пробелы подчеркиванием с помощью

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

если вы используете Arch Linux, вы можете установить rename пакета из AUR обеспечивает

никто не предлагает набирать?

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

Длительным, Но "Работает Без Сюрпризов И Без Установки"

этот скрипт ручками имена файлов с пробелами, кавычками, другой необычно символы и Unicode, работает на нечувствительных к регистру файловых системах и большинстве Unix-y сред у которых установлены bash и awk (т. е. почти все). Он также сообщает о коллизиях, если таковые имеются (оставляя имя файла в верхнем регистре) и, конечно, переименовывает оба файла и каталоги и работает рекурсивно. Наконец, это очень легко адаптируется: вы можете настроить команду find для таргетинга файлов/dirs, которые вы хотите, и вы можете настроить 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?