Lowercasing todos os diretórios sob um diretório

11

Eu quero diminuir o nome de todos os diretórios em um diretório. Com quais comandos eu posso fazer isso?

    
por erkangur 05.01.2011 / 03:34

3 respostas

7

Todos os diretórios em um nível ou recursivamente?

Zsh

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 arquivos que correspondem a um padrão de acordo com o texto de substituição fornecido. -o-i passa a opção -i para cada comando mv sob o capô (veja abaixo). No texto de substituição, $1 , $2 , etc, são os sucessivos grupos entre parênteses no padrão. ** significa todos os diretórios (sub) *, recursivamente. O% final (/) não é um grupo entre parênteses, mas um qualificador glob que significa corresponder apenas aos diretórios. ${2:l} converte $2 em minúsculas.

Portátil

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 confirmação em caso de colisão. Remova o -i para sobrescrever no caso de uma colisão e use yes n | for … . para não ser avisado e não executar qualquer 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 diretórios profundamente aninhados sejam processados antes de seus ancestrais. O processamento de nomes depende de haver um / ; Se você quiser chamar operar no diretório atual, use ./* (adaptar o script de shell para lidar com . ou * é deixado como um exercício para o leitor).

Perl renomear

Aqui eu uso o script de renomeação Perl que o Debian e o Ubuntu enviam como /usr/bin/prename (normalmente disponível como rename também). 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!' {} +
    
por 05.01.2011 / 09:18
4

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, você deve considerar quando já existem dois diretórios que diferem apenas no caso.

Como um verso:

for fd in */; do fd_lower=$(printf %s "$fd" | tr A-Z a-z) && [ "$fd" != "$fd_lower" ] && mv "$fd" "$fd_lower"; done
    
por 05.01.2011 / 04:56
0

Eu aceitei isso como um desafio de primeira 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 identificar os diretórios com letras maiúsculas e, em seguida, reduzi-los via sh -c 'mv {} echo {} | tr [:upper:] [:lower:] '. Acho que usar sh -c é um pouco hack-ish, mas minha cabeça sempre explode quando eu tento escapar coisas para find diretamente.

$ (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 leva a colisões!

    
por 05.01.2011 / 09:45