Як перейменувати всі папки та файли у малі регістри в Linux?


179

Я маю перейменувати повне дерево папок рекурсивно, щоб ніде не видно великі літери (це вихідний код C ++, але це не має значення).

Бонусні бали за ігнорування файлів / папок для керування версіями CVS та Subversion. Кращим способом був би скрипт оболонки, оскільки оболонка повинна бути доступна в будь-якому вікні Linux.

Існували кілька вагомих аргументів щодо деталей перейменування файлів.

  1. Я думаю, що файли з однаковими малими іменами слід перезаписати; це проблема користувача. Перевірившись у файловій системі, що ігнорує регістр, вона також буде перезаписати першу з другою.

  2. Я б розглядав символи AZ і перетворював їх на az, все інше просто викликає проблеми (принаймні, з вихідним кодом).

  3. Сценарій був би необхідний для запуску збірки в системі Linux, тому я думаю, що зміни в файлах контролю CVS або Subversion версії слід опустити. Зрештою, це просто подряпина. Можливо, "експорт" є більш підходящим.


Опублікований раніше буде ідеально нестандартним або з кількома коригуваннями для простих випадків, але є деякі ситуації, які ви, можливо, захочете взяти до уваги перед тим, як запустити пакетне перейменування: 1. Що має статися, якщо у вас є два або більше імен у той самий рівень в ієрархії контурів, який відрізняється лише у випадку, наприклад ABCdef, abcDEFта aBcDeF? Чи повинен сценарій перейменування перервати або просто попередити та продовжити? 2. Як ви визначаєте малі регістри для імен, які не належать США та 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 /' *
Tzury Bar Yochay

3
Обережно, обидві версії не працюватимуть у файловій системі, нечутливій до регістру. У моєму випадку я замінив then-замкнуту лінію на (mv "${SRC}" "${DST}.renametmp" && mv "${DST}.renametmp" "${DST}") || echo "${SRC} was not renamed".
Льоекі

6
Другий підхід для мене неправильно працював із файлами, що містять порожні пробіли (контекст: linux, bash).
дим

4
Використовуючи останню версію перейменування, регулярний вираз не потрібен. Повна команда стає find my_root_dir -depth -exec rename -c {} \;. Додати -f для перейменування, якщо ви користуєтесь нечутливою до регістру файловою системою (наприклад, Mac)
Javache

Другий один працював для мене на Solaris, видаливши -Tз mvкоманди.
Хам

261

Менше все ще мені дуже подобається:

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

У випадку нечутливих файлових систем, таких як HFS + OS X , вам потрібно буде додати -fпрапор:

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

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, що не завжди так. наприклад, моє перейменування debian зовсім інше
Krzysztof Krasoń,

10
На Arch perl-rename
викликується

3
Це не відповідає даній проблемі, оскільки "перейменувати" не є рекурсивним.
Cybolic

89
for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done

Будь ласка, зробіть "mkdir -p A / B / C" перед запуском сценарію.
tzot

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

13
Ого, це досить неефективний спосіб.
Artjom B.

28
Якщо припустити, що обчислювальна ефективність, як правило, НЕ хвилює, коли вам потрібно перейменувати файли в каталозі ... Я думаю, що це, мабуть, найбільш творчо просте рішення в цій темі. Дійсно, щось таке тривіальне не повинно бути таким складним, як демонструють прийняті відповіді, і це рішення є набагато більш пропорційним обсягу думок, які я хочу інвестувати в подібну операцію. +1000
Пітер М. Еліас

27
Ви можете прискорити його з -0(це «нуль», без стиснення) перемикач zip -0 -r foo.zip foo/.
Джонні Балоні

3
Мені довелося змінити понад 50 000 імен файлів у різних структурах каталогу. Оригінальна прийнята відповідь ледь на 10% працювала за 2 хвилини, коли це, -0стиснувшись, було майже миттєвим!
Піон

2
Це не просто неефективно. Це просто неможливо, якщо не вистачає вільного місця для тимчасового архіву.
Віктор Ярема

19

Людина, ви, хлопці, любите надмірно ускладнювати речі ... Використовуйте:

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

... добре вгадайте, що ...


Її було б простіше використовувати renameзамість mv.
Віктор Ярема

14

Це працює, якщо у вас вже є або встановлено команду перейменування (наприклад, через встановлення пива в Mac):

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

9
... якщо ви встановите перейменування спочатку, наприклад, черезbrew install rename
pduersteler

3
перейменуйте 'y / AZ / az /' *
Stephen

4
--lower-case? Принаймні, на жаль, в Arch Linux це не так.

3
renameне є вбудованим баштом. Це зовнішня утиліта, і синтаксис буде відрізнятися залежно від встановленої вами версії. Ця відповідь недостатня як портативне рішення "для всіх ОС на базі Unix", як заявлено.
Corey Goldberg

2
Щойно перевірено, renameкоманда, включена в останню Fedora (28), через util-linux-2.31пакет, не має --lower-caseможливості.
niXar

11

Це працює на 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, і це інший, несумісний інструмент.)


Це працює лише в одному каталозі, а не рекурсивно
Брам

Coool .. Добре працює
Manikandan Ram

6

Найпростіший підхід, який я знайшов у Mac OS X, полягав у використанні пакета перейменування з http://plasmasturm.org/code/rename/ :

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

- примушуйте перейменувати навіть тоді, коли вже існує файл із ім'ям призначення.

--низький регістр Перетворення імен файлів у всі малі регістри.

--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" для пошуку папок.


Ваша перша знахідка не спрацює. Спробуйте "mkdir -p A / B / C", а потім запустіть свій сценарій.
tzot

4

В оригінальному запитанні було задано ігнорування каталогів SVN та CVS, що можна зробити, додавши в команду find -prune. Наприклад, ігнорувати 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, звичайно, сценарій вище)


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 у першій знахідці.


1
Єдине, що працювало на Mac із цілого потоку. Дякую купи!
YemSalat

2

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

Спочатку переконайтеся, що zmvзавантажено.

autoload -U zmv

Також переконайтеся, що extendedglobввімкнено:

setopt extendedglob

Потім використовуйте:

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

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


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

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


Це не працює: він перейменовує каталог перед тим, як оперувати вмістом, так що наступна спроба вмісту не вдасться.
Чорнослив

Додавання -depthвиправлень, що! Це дійсно швидке рішення, але, звичайно, не використовуючи -print0опцію пошуку, це не найнадійніше
jpaugh

@jpaugh Ні, це не так. Він спробує перейменувати ./FOO/BAR у ./foo/bar, але ./foo ще не існує.
ойсин

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 %' You can run without -Ilikefind . -iname "*.py" -type f | xargs -I% rename -c -f "%"
akilesh raj

2

Використовуйте набір :

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?


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

)


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 замість пошуку ...- чорнослив, тому що це простіше. Для великих каталогів, для f in ... може переповнювати деякі буфери оболонок. Використовуйте знайти ... | при цьому читайте, щоб уникнути цього.

І так, це будуть халодні файли, які відрізняються лише у випадку ...


По-перше: find YOURDIR -type d | sort -rзанадто багато клопоту. Ти хочеш find YOURDIR -depth -type d. По-друге, find -type fПОВИНЕН запуститись після того, як каталоги були перейменовані.
tzot

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або будь-якою оболонкою на свій смак.


0

Слугуйте перейменування (регулярний вираз)

Це не саме те, що просила ОП, але те, що я сподівався знайти на цій сторінці:

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

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

2
"не зовсім"? це зовсім не те, про що просила ОП
Корі Голдберг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.