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

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

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

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

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

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

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

в нечувствительных к регистру файловых системах, таких как 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 вместо \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/'*


использование имени файла фиксаж Ларри Уолла

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


исходный вопрос, заданный для игнорирования каталогов 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" оба возвращают список папок в удобном порядке для переименования, я предпочел использование "- depth " для поиска папок.


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

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

обратите внимание на действие-depth в первом поиске.


это работает на 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? Должен ли скрипт rename прерваться или просто предупредить и продолжить?

  2. как сделать определите нижний регистр для нас-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

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

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


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

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

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

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 обеспечивает renamexm команда as /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

Вы можете принести образец разработчики.mp3 файл из здесь, если нужно ;)


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

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?