Відповіді:
Всі каталоги на одному рівні чи рекурсивно?
На одному рівні:
autoload zmv
zmv -o-i -Q 'root/(*)(/)' 'root/${1:l}'
Рекурсивно:
zmv -o-i -Q 'root/(**/)(*)(/)' 'root/$1${2:l}'
Пояснення: zmv
перейменовує файли, що відповідають шаблону відповідно до заданого тексту заміни. -o-i
передає -i
опцію до кожної mv
команди під кришкою (див. нижче). У тексті заміни, $1
, $2
і т.д., є послідовною Дужками групи в шаблоні. **
означає всі (під) * каталоги, рекурсивно. Фінал (/)
- це не дужка група, а глобальний класифікатор, що означає відповідати лише каталогів. ${2:l}
перетворюється $2
в малі регістри.
На одному рівні:
for x in root/*/; do mv -i "$x" "$(printf %s "$x" | tr '[:upper:]' '[:lower:]')"; done
Фінал /
обмежує відповідність каталогів і mv -i
змушує просити підтвердження у випадку зіткнення. Видаліть -i
перезапис у разі зіткнення та використовуйте yes n | for …
. не вимагати і не виконувати перейменування, яке могло б зіткнутися.
Рекурсивно:
find root/* -depth -type d -exec sh -c '
t=${0%/*}/$(printf %s "${0##*/}" | tr "[:upper:]" "[:lower:]");
[ "$t" = "$0" ] || mv -i "$0" "$t"
' {} \;
Використання -depth
гарантує, що глибоко вкладені каталоги обробляються перед своїми предками. Обробка імен покладається на те, що існує /
; якщо ви хочете, щоб виклик працював у поточному каталозі, використовуйте ./*
(адаптуючи сценарій оболонки, щоб впоратися з .
чи *
залишається як вправа для читача).
Тут я використовую сценарій перейменування Perl, який Debian і Ubuntu поставляють /usr/bin/prename
(як правило, доступні також rename
). На одному рівні:
rename 's!/([^/]*/?)$!\L/$1!' root/*/
Рекурсивно, з bash ≥4 або zsh:
shopt -s globstar # only in bash
rename 's!/([^/]*/?)$!\L/$1!' root/**/*/
Рекурсивно, портативно:
find root -depth -type d -exec rename -n 's!/([^/]*/?)$!\L/$1!' {} +
-execdir
що дивовижно: unix.stackexchange.com/questions/5412/… Потім я виявив, що в ньому є якесь PATH
божевілля, і було сумно :-(
Не існує жодної команди, яка б це зробила, але ви можете зробити щось подібне:
for fd in */; do
#get lower case version
fd_lower=$(printf %s "$fd" | tr A-Z a-z)
#if it wasn't already lowercase, move it.
[ "$fd" != "$fd_lower" ] && mv "$fd" "$fd_lower"
done
Якщо вам потрібно, щоб він був надійним, вам слід врахувати, коли вже є два каталоги, які відрізняються лише у випадку.
Як однолінійний:
for fd in */; do fd_lower=$(printf %s "$fd" | tr A-Z a-z) && [ "$fd" != "$fd_lower" ] && mv "$fd" "$fd_lower"; done
for file in * ; do if [ -d "$file" ] ; then dest="$(echo $file | sed y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/)" ; [ "$dest" != "$file" ] && mv "$file" "$dest" ; fi ; done
find -type d
, але не зміг його отримати
for fd in */;
, таким чином уникаючи необхідності перевірки, якби це був каталог, але я не маю уявлення, чи був цей інстинкт хорошим.
tr
вже чекає діапазон, так tr '[A-Z]' '[a-z]'
перекладається [
з [
і ]
в ]
мимохідь. Це марно, але нешкідливо; проте без лапок оболонка розширить дужки, якби в поточному каталозі був файл з назвою з великої літери.
Я сприйняв це як однолінійне завдання :) Спочатку встановіть тестовий випадок:
$ for d in foo Bar eVe; do mkdir -p dcthis/$d; touch dcthis/a${d}.txt; done
$ ls dcthis/
Bar aBar.txt aeVe.txt afoo.txt eVe foo
Я використовую find
для виявлення каталогів з великої літери, а потім зволікаю їх через . Здогадуюсь, це використання трохи злому, але моя голова завжди вибухає, коли я намагаюся уникати речей безпосередньо.sh -c 'mv {}
echo {} | tr [:upper:] [:lower:]
'sh -c
find
$ (cd dcthis && find . -maxdepth 1 -type d -path '*[A-Z]*' -exec sh -c 'mv {} `echo {} | tr [:upper:] [:lower:]`' \;)
$ ls dcthis/
aBar.txt aeVe.txt afoo.txt bar eve foo
Будьте попереджені: Це рішення не перевіряє, чи призводить до зниження чисельності!
mv -i
. Більша проблема полягає в тому, що ви не використовували належне котирування, тому ваша команда не вдасться, якщо в імені є \[*?
десь спеціальні символи (пробіл або ). Немає сенсу користуватися, find
якщо не повторюватись, і тоді вам потрібно find -depth
, і -path
треба -name
. Дивіться мою відповідь на робочих прикладах.
mv -i
після написання цього. Хороше запитання з посиланням на ...
find -execdir
| перейменувати
Це був би найкращий спосіб зробити це, якби не відносне безумство шляху, оскільки це дозволяє уникнути Perge regex fu діяти лише на базове ім'я:
PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
find a -depth -execdir rename 's/(.*)/\L$1/' '{}' \;
-execdir
Перший cd
s в каталог перед виконанням лише базового імені.
На жаль, я не можу позбутися цієї PATH
хакерської частини, find -execdir
відмовляється робити що-небудь, якщо у вас є відносний шлях у PATH
...: /ubuntu/621132/why-using-the-execdir-action- це-небезпечно для каталогу, яке-є-в-шляху / 1109378 # 1109378
mv -i a a
дайте "mv: перейменувати a в a / a: Недійсний аргумент".