Respostas:
Todos os diretórios em um nível ou recursivamente?
Em um nível:
autoload zmv
zmv -o-i -Q 'root/(*)(/)' 'root/${1:l}'
Recursivamente:
zmv -o-i -Q 'root/(**/)(*)(/)' 'root/$1${2:l}'
Explicações: zmv
renomeia os arquivos que correspondem a um padrão de acordo com o texto de substituição fornecido. -o-i
passa a -i
opção para cada mv
comando sob o capô (veja abaixo). No texto de substituição, $1
, $2
, etc, são os grupos entre parênteses sucessivas no padrão. **
significa todos os (sub) * diretórios, recursivamente. A final (/)
não é um grupo entre parênteses, mas um qualificador global que significa corresponder apenas aos diretórios. ${2:l}
converte $2
para minúsculas.
Em um nível:
for x in root/*/; do mv -i "$x" "$(printf %s "$x" | tr '[:upper:]' '[:lower:]')"; done
O final /
restringe a correspondência aos diretórios e mv -i
solicita a confirmação em caso de colisão. Remova o -i
para substituir em caso de colisão e use yes n | for …
. para não ser solicitado e não executar nenhuma renomeação que colidiria.
Recursivamente:
find root/* -depth -type d -exec sh -c '
t=${0%/*}/$(printf %s "${0##*/}" | tr "[:upper:]" "[:lower:]");
[ "$t" = "$0" ] || mv -i "$0" "$t"
' {} \;
O uso de -depth
garante que os diretórios profundamente aninhados sejam processados antes de seus ancestrais. O processamento de nomes depende da existência de um /
; se você quiser chamar de operar no diretório atual, use ./*
(adaptar o shell script para lidar com .
ou *
é deixado como um exercício para o leitor).
Aqui eu uso o script de renomeação do Perl que o Debian e o Ubuntu lançam como /usr/bin/prename
(normalmente também disponíveis rename
). Em um nível:
rename 's!/([^/]*/?)$!\L/$1!' root/*/
Recursivamente, com bash ≥4 ou zsh:
shopt -s globstar # only in bash
rename 's!/([^/]*/?)$!\L/$1!' root/**/*/
Recursivamente, portably:
find root -depth -type d -exec rename -n 's!/([^/]*/?)$!\L/$1!' {} +
-execdir
que é incrível: unix.stackexchange.com/questions/5412/… Descobri que ele tinha uma PATH
loucura e ficou triste :-(
Não há um único comando que faça isso, mas você pode fazer algo assim:
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
Se você precisar que ele seja robusto, considere quando já existem dois diretórios que diferem apenas no caso.
Como uma linha:
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
, mas não consegui entender
for fd in */;
, evitando assim a necessidade de verificar se era um diretório, mas não tenho idéia se esse instinto era bom.
tr
já espera um intervalo, então tr '[A-Z]' '[a-z]'
traduz [
para [
e ]
para ]
de passagem. Isso é inútil, mas inofensivo; no entanto, sem as aspas, o shell expandiria os colchetes se houvesse um arquivo com o nome de uma letra maiúscula no diretório atual.
Tomei isso como um desafio de uma linha :) Primeiro, estabeleça um caso de teste:
$ 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
Eu uso find
para localizar os diretórios com letras maiúsculas e depois colocá-los em minúsculas . Acho que usar é um pouco hack-ish, mas minha cabeça sempre explode quando tento escapar das coisas diretamente.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
Esteja avisado: Esta solução não verifica se o downcasing causa colisões!
mv -i
. Um problema maior é que você não usou a citação adequada; portanto, seu comando falhará se houver caracteres especiais (espaço em branco ou \[*?
) em qualquer lugar do nome. Não há nenhum ponto de usar, a find
menos que seja recorrente, e então você precisa find -depth
e -path
deve ser -name
. Veja minha resposta para exemplos de trabalho.
mv -i
depois de escrever isso. Bom ponto com a citação ...
find -execdir
| renomear
Essa seria a melhor maneira de fazer isso, se não fosse a loucura relativa do caminho, pois evita que o Regex fu do Perl atue apenas no nome da base:
PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
find a -depth -execdir rename 's/(.*)/\L$1/' '{}' \;
-execdir
primeiro cd
s no diretório antes de executar apenas no nome da base.
Infelizmente, não consigo me livrar dessa PATH
parte de hackers, find -execdir
se recusa a fazer qualquer coisa se você tiver um caminho relativo em PATH
...: /ubuntu/621132/why-using-the-execdir-action- é inseguro para o diretório que está no caminho / 1109378 # 1109378
mv -i a a
dê "mv: renomeie a para a / a: argumento inválido".