Pergunta

Eu tenho que mudar o nome de uma árvore de pastas de forma recursiva para que nenhuma letra maiúscula aparece em qualquer lugar (sourcecode-lo do C ++, mas isso não importa). Os pontos de bônus para ignorar CVS e arquivos de controle SVN / pastas. forma preferida seria um shell script, desde shell deve estar disponível em qualquer caixa de Linux.

Houve alguns argumentos válidos sobre os detalhes da mudança de nome de arquivo.

  1. Eu acho que arquivos com os mesmos nomes minúsculas deve ser substituído, é o problema do usuário. Quando check-out em um sistema de arquivos caso-ignorando iria substituir o primeiro com o último, também.

  2. Eu consideraria caracteres A-Z e transformá-los para a-z, tudo o resto é apenas chamar para problemas (pelo menos com o código fonte).

  3. O script seria necessário para executar uma compilação em um sistema Linux, então eu acho que muda para CVS ou SVN arquivos de controle deve ser omitido. Afinal de contas, é apenas uma verificação do zero. Talvez uma "exportação" é mais apropriado.

Foi útil?

Solução

A versão concisa usando o comando "rename".

find my_root_dir -depth -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;

Isso evita problemas com diretórios sendo renomeado antes de arquivos e tentar mover arquivos em diretórios não-existente (por exemplo "A/A" em "a/a").

Ou, um mais detalhado versão sem usar "rename".

for SRC in `find my_root_dir -depth`
do
    DST=`dirname "${SRC}"`/`basename "${SRC}" | tr '[A-Z]' '[a-z]'`
    if [ "${SRC}" != "${DST}" ]
    then
        [ ! -e "${DST}" ] && mv -T "${SRC}" "${DST}" || echo "${SRC} was not renamed"
    fi
done

P. S.

O último permite mais flexibilidade com comando de movimento (e. G. "svn mv").

Outras dicas

menor ainda eu gosto bastante

rename 'y/A-Z/a-z/' *

No caso insensível sistemas de arquivos, como de OS X HFS +, você vai querer adicionar o sinalizador -f

rename -f 'y/A-Z/a-z/' *
for f in `find`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done

Simplesmente tente seguir se você não precisa se preocupar com eficiência.

zip -r foo.zip foo/*
unzip -LL foo.zip

A maioria das respostas acima são perigosos porque eles não lidam com nomes que contêm caracteres estranhos. Sua aposta mais segura para este tipo de coisa é usar a opção -print0 de encontrar, o que irá terminar nomes de arquivos com NUL ASCII em vez de \ n. Aqui eu enviar este script, que apenas os arquivos alterar e não nomes de diretórios de modo a não encontrar confundir.

find .  -type f -print0 | xargs -0n 1 bash -c \
's=$(dirname "$0")/$(basename "$0"); 
d=$(dirname "$0")/$(basename "$0"|tr "[A-Z]" "[a-z]"); mv -f "$s" "$d"'

Eu testei e funciona com nomes de arquivos que contenham espaços, todos os tipos de citações, etc. Isto é importante porque se você correr, como root, um daqueles outro script em uma árvore que inclui o arquivo criado por:

touch \;\ echo\ hacker::0:0:hacker:\$\'\057\'root:\$\'\057\'bin\$\'\057\'bash

... bem adivinhem ...

Isso funciona se você já tiver ou configurar renomeação comando (por exemplo, através de infusão instalar no Mac):

rename --lower-case --force somedir/*

Homem vocês gostariam de mais coisas complicar ..

renomeação 'y / A-Z / a-z /' *

Isso funciona em CentOS / Red Hat ou outras distribuições sem o script Perl rename:

for i in $( ls | grep [A-Z] ); do mv -i "$i" "`echo $i | tr 'A-Z' 'a-z'`"; done

Fonte: https://linuxconfig.org/rename- all-files-de-maiúscula-to-minúsculas-personagens

(em alguns distros o comando rename padrão vem do util-linux, e que é uma ferramenta diferente, incompatível)

Usando fixer nome de Larry Wall

$op = shift or die $help;
chomp(@ARGV = <STDIN>) unless @ARGV;
for (@ARGV) {
    $was = $_;
    eval $op;
    die $@ if $@;
    rename($was,$_) unless $was eq $_;
}

é tão simples como

find | fix 'tr/A-Z/a-z/'

(onde correção é, naturalmente, o script acima)

A pergunta original pediu ignorando SVN e CVS diretórios, o que pode ser feito adicionando -prune para o comando find. Por exemplo ignorar CVS:

find . -name CVS -prune -o -exec mv '{}' `echo {} | tr '[A-Z]' '[a-z]'` \; -print

[editar] Eu tentei isso, e incorporação a tradução minúscula dentro do achado não funcionou por razões que eu realmente não entendo. Assim, alterar isso:

$> cat > tolower
#!/bin/bash
mv $1 `echo $1 | tr '[:upper:]' '[:lower:]'`
^D
$> chmod u+x tolower 
$> find . -name CVS -prune -o -exec tolower '{}'  \;

Ian

Aqui está a minha solução sub-óptima, usando um script shell bash:

#!/bin/bash
# first, rename all folders
for f in `find . -depth ! -name CVS -type d`; do
   g=`dirname "$f"`/`basename "$f" | tr '[A-Z]' '[a-z]'`
   if [ "xxx$f" != "xxx$g" ]; then
      echo "Renaming folder $f"
      mv -f "$f" "$g"
   fi
done

# now, rename all files
for f in `find . ! -type d`; do
   g=`dirname "$f"`/`basename "$f" | tr '[A-Z]' '[a-z]'`
   if [ "xxx$f" != "xxx$g" ]; then
      echo "Renaming file $f"
      mv -f "$f" "$g"
   fi
done

Editar: Eu fiz algumas modificações com base nas sugestões até agora. Agora pastas são todos renomeada corretamente, mv não está fazendo perguntas quando as permissões não corresponderem, e pastas CVS não são renomeados (arquivos de controle CVS dentro dessa pasta ainda são renomeados, infelizmente).

Edit:. Uma vez que "encontrar -depth" e "encontrar | tipo -r" tanto retorno da lista de pastas em uma ordem utilizável para renomear, eu preferiu usar "aprofundada" para pesquisar pastas

Este é um pequeno shell script que faz o que você solicitado:

root_directory="${1?-please specify parent directory}"
do_it () {
    awk '{ lc= tolower($0); if (lc != $0) print "mv \""  $0 "\" \"" lc "\"" }' | sh
}
# first the folders
find "$root_directory" -depth -type d | do_it
find "$root_directory" ! -type d | do_it

Observe a ação -depth no primeiro achado.

A postado anteriormente irá funcionar perfeitamente fora da caixa ou com alguns ajustes para casos simples, mas existem algumas situações que você pode querer ter em conta antes de executar o lote renomear:

  1. O que deve acontecer se você tiver dois ou mais nomes no mesmo nível na hierarquia caminho que diferem apenas por caso, como ABCdef, abcDEF e aBcDeF? Se o script de renomeação abortar ou apenas advertir e continuar?

  2. Como você define minúsculas para nomes não US-ASCII? Se esses nomes podem estar presentes, se uma verificação e excluir passar ser realizada pela primeira vez?

  3. Se você estiver executando uma operação de renomeação em CVS ou SVN cópias de trabalho, você pode corromper a cópia de trabalho se você alterar o caso sobre os nomes dos arquivos ou diretórios. Se o script também encontrar e ajustar arquivos administrativos internos, como .svn / entradas ou CVS / Entries?

for f in `find -depth`; do mv ${f} ${f,,} ; done

find -depth imprime cada arquivo e diretório, com conteúdo de um diretório impresso antes o próprio diretório. ${f,,} letras minúsculas o nome do arquivo.

Com MacOS,

Instale o pacote rename,

brew install rename

Use,

find . -iname "*.py" -type f | xargs -I% rename -c -f  "%"                       

Este comando encontrar todos os arquivos com uma extensão *.py e converte os nomes de arquivos em minúsculas.

`f` - forces a rename

Por exemplo,

$ find . -iname "*.py" -type f
./sample/Sample_File.py
./sample_file.py
$ find . -iname "*.py" -type f | xargs -I% rename -c -f  "%"
$ find . -iname "*.py" -type f
./sample/sample_file.py
./sample_file.py

Não portátil, Zsh apenas, mas concisa bonita.

Em primeiro lugar, certifique-se zmv é carregado.

autoload -U zmv

Além disso, certifique extendedglob está em:

setopt extendedglob

Em seguida, use:

zmv '(**/)(*)~CVS~**/CVS' '${1}${(L)2}'

Para minúsculas recursivamente arquivos e diretórios onde o nome não é CVS .

No OSX, mv -f mostra "mesmo arquivo" erro, então eu mudar o nome duas vezes.

for i in `find . -name "*" -type f |grep -e "[A-Z]"`; do j=`echo $i | tr '[A-Z]' '[a-z]' | sed s/\-1$//`; mv $i $i-1; mv $i-1 $j; done

Eu precisava fazer isso em uma configuração cygwin no Windows 7 e descobriu que eu tenho erros de sintaxe com as sugestões acima que eu tentei (embora eu possa ter perdido uma opção de trabalho) no entanto esta solução de linha reta de Fórum ubutu trabalharam fora da lata: -)

ls | while read upName; do loName=`echo "${upName}" | tr '[:upper:]' '[:lower:]'`; mv "$upName" "$loName"; done

(NB I tinha anteriormente substituir os espaços em branco com sublinhados usando

for f in *\ *; do mv "$f" "${f// /_}"; done

eu iria chegar para python nesta situação, para evitar otimista assumindo caminhos sem espaços ou barras. Eu também constatou que python2 tende a ser instalado em mais lugares do que rename.

#!/usr/bin/env python2
import sys, os

def rename_dir(directory):
  print('DEBUG: rename('+directory+')')
  # rename current directory if needed
  os.rename(directory, directory.lower())
  directory = directory.lower()

  # rename children
  for fn in os.listdir(directory):
    path = os.path.join(directory, fn)
    os.rename(path, path.lower())
    path = path.lower()

    # rename children within, if this child is a directory
    if os.path.isdir(path):
        rename_dir(path)

# run program, using the first argument passed to this python script as the name of the folder
rename_dir(sys.argv[1])

moroso mas "funciona sem surpresas e nenhuma instalação"

Este script alças nomes de arquivos com espaços, citações, outros incomuns caracteres e Unicode, funciona em casos de sistemas de arquivos insensíveis e ambientes mais Unix-y que tem bash e AWK instalado ( ou seja, quase todos). Ele também relata colisões se houver (deixando o nome do arquivo em maiúsculas) e, claro, renomeia ambos os arquivos e diretórios e trabalha de forma recursiva. Finalmente é altamente adaptável: você pode ajustar o comando find para direcionar os arquivos / diretórios que você deseja e você pode ajustar awk para fazer outras manipulações de nome. Note-se que por "alças Unicode" Quero dizer que ele vai realmente converter seu caso (não ignorá-los como respostas que usam tr).

# adapt the following command _IF_ you want to deal with specific files/dirs
find . -depth -mindepth 1 -exec bash -c '
  for file do
    # adapt the awk command if you wish to rename to something other than lowercase
    newname=$(dirname "$file")/$(basename "$file" | awk "{print tolower(\$0)}")
    if [ "$file" != "$newname" ] ; then
        # the extra step with the temp filename is for case-insensitive filesystems
        if [ ! -e "$newname" ] && [ ! -e "$newname.lcrnm.tmp" ] ; then
           mv -T "$file" "$newname.lcrnm.tmp" && mv -T "$newname.lcrnm.tmp" "$newname" 
        else
           echo "ERROR: Name already exists: $newname"
        fi
    fi    
  done
' sh {} +

Referências

Meu script é baseado nessas respostas excelentes:

https://unix.stackexchange.com/questions / 9496 / loop-through-arquivos-com-espaços-in-the-nomes

Como converter uma string para minúsculas em Bash?

mais simples abordagem que eu encontrei em MacOSX era usar o pacote de renomeação de http://plasmasturm.org/code / renomear / :

brew install rename
rename --force --lower-case --nows *

-. Vigor Rename mesmo quando um arquivo com o nome de destino já existe

-. Nomes minúsculas de arquivo converter para todas as minúsculas

-. Agoras Substitua todas as sequências de espaços em branco no nome do arquivo com caracteres de sublinhado único

( find YOURDIR -type d | sort -r;
  find yourdir -type f ) |
grep -v /CVS | grep -v /SVN |
while read f; do mv -v $f `echo $f | tr '[A-Z]' '[a-z]'`; done

Primeiro renomear os diretórios-se inferior tipo -r (onde -depth não está disponível), então os arquivos. Em seguida, grep -v / CVS em vez de encontrar ...- ameixa porque é mais simples. Para grandes diretórios, for f in ... pode transbordar alguns buffers de shell. Use encontrar ... | enquanto leitura para evitar isso.

E sim, isso vai espancar arquivos que diferem apenas no caso ...

Slugify Rename (regex)

Não é exatamente o que o OP pediu, mas o que eu estava esperando para encontrar nesta página:

A "slugify" versão para renomear arquivos para que eles são semelhantes aos URLs
(Ou seja, incluem apenas alfanuméricos, pontos e traços):

rename "s/[^a-zA-Z0-9\.]+/-/g" filename

Se você usar Arch Linux , você pode instalar rename pacote de AUR que fornece comando renamexm como executável /usr/bin/renamexm e sua página o manual junto com ele.

É realmente poderoso instrumento para arquivos rapidamente renomear e diretórios.

Converter para minúsculas

rename -l Developers.mp3 # or --lowcase

Converter em Maiúsculas

rename -u developers.mp3 # or --upcase, long option

Outras opções

-R --recursive # directory and its children

-t --test # dry run, output but don't rename

-o --owner # change file owner as well to user specified

-v --verbose # output what file is renamed and its new name

-s/str/str2 # substite string on pattern

--yes # Confirm all actions

Você pode buscar a amostra Developers.mp3 arquivo de aqui , se necessário;)

Ninguém sugerindo typeset?

typeset -l new        # always lowercase
find $topPoint |      # not using xargs to make this more readable
  while read old
  do mv "$old" "$new" # quotes for those annoying embedded spaces
  done

Em emulações janelas como o bash git este pode falhar porque o Windows não é sensível a maiúsculas sob o capô. Para aqueles, adicione um passo que mv é o arquivo para outro nome em primeiro lugar, como "$ old.tmp", em seguida, para US $ nova.

Isso funciona muito bem no MacOS demasiado:

ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"
find . -depth -name '*[A-Z]*'|sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'|sh

Eu não tentei os scripts mais elaboradas mencionados aqui, mas nenhuma das versões individuais de linha de comando funcionou para mim no meu Synology NAS. rename não está disponível, e muitas das variações de find falhar porque parece manter o nome antigo do caminho já renomeado (por exemplo, se encontra ./FOO seguido por ./FOO/BAR, renomeando ./FOO para ./foo ainda vai continuar a lista ./FOO/BAR embora esse caminho não é mais válido). Comando acima funcionou para mim, sem quaisquer problemas.

O que se segue é uma explicação de cada parte do comando:


find . -depth -name '*[A-Z]*'

Este vai encontrar qualquer arquivo do diretório atual (mudança . para qualquer diretório que você deseja processo), usando uma busca em profundidade (por exemplo., Ele irá listar ./foo/bar antes ./foo), mas somente para arquivos que contêm uma letra maiúscula personagem. O filtro -name só se aplica ao nome do arquivo base, não o caminho completo. Portanto, este irá listar ./FOO/BAR mas não ./FOO/bar. Este é ok, já que não pretende mudar o nome ./FOO/bar. Queremos mudar o nome ./FOO embora, mas que um é listado mais tarde (é por isso que -depth é importante).

Esta comand em si é particularmente útil para encontrar os arquivos que você deseja renomear no primeiro lugar. Utilize este depois de o comando completo de mudança de nome para procurar arquivos que ainda não foram substituídos devido a colisões de nome de arquivo ou erros.


sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'

Esta parte lê os arquivos produzidos pela find e formata-los em um comando mv usando uma expressão regular. A opção -n pára sed de imprimir a entrada, eo comando p na busca e substituição saídas regex o texto substituído.

O regex em si consiste em duas capturas: a parte até o último / (que é o diretório do arquivo), e o próprio nome. O diretório é deixada intacta, mas o nome do arquivo é transformado em minúsculas. Então, se as saídas find ./FOO/BAR, ele se tornará mv -n -v -T ./FOO/BAR ./FOO/bar. A opção -n de mv faz arquivos minúsculas certeza existentes não são substituídos. A opção -v torna saída mv cada mudança que faz (ou não faz - se ./FOO/bar já existe, ele produz algo como ./FOO/BAR -> ./FOO/BAR, notando que nenhuma alteração foi feita). O -T é muito importante aqui - ele trata o arquivo de destino como um diretório. Isso fará com que ./FOO/BAR não é movido para ./FOO/bar se esse diretório acontece de existir.

Use este juntamente com find para gerar uma lista de comandos que serão executados (útil para verificar o que vai ser feito sem realmente fazê-lo)


sh

Esta auto-explicativo. Ele rotas todos os comandos mv gerados para o interpretador shell. Você pode substituí-lo com bash ou qualquer shell de seu gosto.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top