Нижний регистр всех каталогов в каталоге

12

Я хочу в нижнем регистре имя каждого каталога в каталоге. С помощью каких команд я могу это сделать?

erkangur
источник

Ответы:

10

Все каталоги на одном уровне или рекурсивно?

Zsh

На одном уровне:

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и т. Д., Есть последовательные заключенные в скобки группы в шаблоне. **означает все (под) * каталоги, рекурсивно. Финал (/)- это не группа в скобках, а квалификатор glob, означающий совпадение только с каталогами. ${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 переименовать

Здесь я использую сценарий переименования 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!' {} +
Жиль "ТАК - перестань быть злым"
источник
По крайней мере, в OS X это не удастся, если какие-либо каталоги уже имеют нижний регистр: mv -i a aукажите «mv: переименуйте a в / a: неверный аргумент».
Янус
@Janus: Да, вы получаете сообщение об ошибке, которое выглядит ужасно (хотя и безвредно в командной строке). Но в любом случае я должен был использовать ZMV, который заботится об этом случае.
Жиль "ТАК - перестань быть злым"
Затем я узнал о том, -execdirчто удивительно: unix.stackexchange.com/questions/5412/… Затем я обнаружил, что в нем есть какое-то PATHбезумие и он грустит :-(
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件
4

Нет ни одной команды, которая это сделает, но вы можете сделать что-то вроде этого:

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, но не смог понять
Майкл Мрозек
1
Мой инстинкт должен был бы быть for fd in */;таким, чтобы избежать необходимости проверки, если это был каталог, но я понятия не имею, был ли этот инстинкт хорошим.
Стивен Д
Да, для ... * / было бы лучше.
Шон Дж. Гофф
1
@Shawn: trуже ожидает диапазон, так tr '[A-Z]' '[a-z]'переводится [с [и ]в ]мимоходом. Это бесполезно, но безвредно; однако без кавычек оболочка расширит скобки, если в текущем каталоге будет файл с именем, состоящим из одной заглавной буквы.
Жиль "ТАК - прекрати быть злым"
0

Я воспринял это как однострочную задачу :) Сначала создайте контрольный пример:

$ 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 -cfind

$ (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. Смотрите мой ответ для рабочих примеров.
Жиль "ТАК - перестань быть злым"
@Giles. Благодарность! Я видел твои mv -iпосле написания этого. Хороший вопрос с цитатой ...
Янус
0

find -execdir| переименование

Это был бы лучший способ сделать это, если бы не безумие относительного пути, так как Perl regex fu не действует только на базовое имя:

PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
  find a -depth -execdir rename 's/(.*)/\L$1/' '{}' \;

-execdirсначала cdв каталог, прежде чем выполнять только по базовому имени.

К сожалению, я не могу избавиться от этой PATHхакерской части, find -execdirотказывается что-либо делать, если у вас есть относительный путь в PATH...: /ubuntu/621132/why-using-the-execdir-action- является нестабильным-для-каталог-который-является-в-пути / 1109378 # 1109378

Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件
источник