Minúsculas em todos os diretórios em um diretório

12

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

erkangur
fonte

Respostas:

10

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: zmvrenomeia os arquivos que correspondem a um padrão de acordo com o texto de substituição fornecido. -o-ipassa a -iopção para cada mvcomando 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 $2para 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 -isolicita a confirmação em caso de colisão. Remova o -ipara 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 -depthgarante 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).

Renomear Perl

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!' {} +
Gilles 'SO- parar de ser mau'
fonte
Pelo menos no OS X, isso falhará se algum diretório já estiver em letras minúsculas: mv -i a adê "mv: renomeie a para a / a: argumento inválido".
Janus
@ Janus: Certo, você recebe uma mensagem de erro feia (embora inofensiva na linha de comando). Mas de qualquer maneira eu deveria ter usado o zmv, que cuida desse caso.
Gilles 'SO- stop be evil'
Descobri então o -execdirque é incrível: unix.stackexchange.com/questions/5412/… Descobri que ele tinha uma PATHloucura e ficou triste :-(
Ciro Santilli escreveu
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, 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
Shawn J. Goff
fonte
Isso apareceu no meio da minha escrita a sugestão (basicamente a mesma):for file in * ; do if [ -d "$file" ] ; then dest="$(echo $file | sed y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/)" ; [ "$dest" != "$file" ] && mv "$file" "$dest" ; fi ; done
frabjous
Eu estava tentando resolver o problema find -type d, mas não consegui entender
Michael Mrozek
1
Meu instinto deveria ter sido feito 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.
Steven D
Sim, por ... * / seria melhor.
precisa saber é o seguinte
1
@ Shawn: trjá 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.
Gilles 'SO- stop be evil'
0

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 findpara 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 -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

Esteja avisado: Esta solução não verifica se o downcasing causa colisões!

Janus
fonte
verificação de colisões é fácil: 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 findmenos que seja recorrente, e então você precisa find -depthe -pathdeve ser -name. Veja minha resposta para exemplos de trabalho.
Gilles 'SO- stop be evil'
@Giles. Obrigado! Eu vi o seu mv -idepois de escrever isso. Bom ponto com a citação ...
Janus
0

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/' '{}' \;

-execdirprimeiro cds no diretório antes de executar apenas no nome da base.

Infelizmente, não consigo me livrar dessa PATHparte de hackers, find -execdirse 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

Ciro Santilli adicionou uma nova foto
fonte