Como renomeio todas as pastas e arquivos para minúsculas no Linux?


179

Eu tenho que renomear uma árvore de pastas completa recursivamente para que nenhuma letra maiúscula apareça em qualquer lugar (é o código-fonte C ++, mas isso não deve importar).

Pontos de bônus por ignorar arquivos / pastas de controle de versão do CVS e Subversion. A maneira preferida seria um script de shell, já que um shell deve estar disponível em qualquer caixa do Linux.

Houve alguns argumentos válidos sobre os detalhes da renomeação do arquivo.

  1. Eu acho que arquivos com os mesmos nomes em minúsculas devem ser substituídos; é problema do usuário. Quando feito check-out em um sistema de arquivos que ignora maiúsculas e minúsculas, ele substitui o primeiro pelo último também.

  2. Eu consideraria os caracteres AZ e os transformaria em az; tudo o resto está apenas pedindo problemas (pelo menos com o código-fonte).

  3. O script seria necessário para executar uma compilação em um sistema Linux, portanto, acho que as alterações nos arquivos de controle de versão do CVS ou Subversion devem ser omitidas. Afinal, é apenas uma verificação final. Talvez uma "exportação" seja mais apropriada.


A postagem anterior funcionará perfeitamente pronta para uso ou com alguns ajustes para casos simples, mas há algumas situações que você pode levar em consideração antes de executar a renomeação do lote: 1. O que deve acontecer se você tiver dois ou mais nomes em o mesmo nível na hierarquia de caminhos que diferem apenas por caso, como ABCdef, abcDEFe aBcDeF? O script de renomeação deve abortar ou apenas avisar e continuar? 2. Como você define letras minúsculas para nomes que não sejam US-ASCII? Se tais nomes estiverem presentes, deve-se primeiro verificar e excluir o passe? 3. Se você estiver executando uma operação de renomeação
Mihai Limbășan

Respostas:


179

Uma versão concisa usando o "rename"comando:

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

Isso evita problemas com os diretórios sendo renomeados antes dos arquivos e tentando mover os arquivos para diretórios não existentes (por exemplo, "A/A"para "a/a").

Ou, uma versão mais detalhada 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

PS

Este último permite mais flexibilidade com o comando mover (por exemplo, "svn mv").


36
o uso de renomear pode ser feito dessa maneira, bem como renomear 'y / AZ / az /' *
Tzury Bar Yochay

3
Cuidado, ambas as versões não funcionarão em um sistema de arquivos que não diferencia maiúsculas de minúsculas. No meu caso, substituí a thenlinha fechada por (mv "${SRC}" "${DST}.renametmp" && mv "${DST}.renametmp" "${DST}") || echo "${SRC} was not renamed".
Lloeki

6
A segunda abordagem não funcionou corretamente para mim com arquivos contendo espaços em branco (contexto: linux, bash).
Dim

4
Usando a última versão da renomeação, nenhum regex é necessário. O comando completo se torna find my_root_dir -depth -exec rename -c {} \;. Adicionar -f para mudar o nome se você estiver em um sistema de arquivos de maiúsculas e minúsculas (por exemplo, Mac)
Javache

O segundo funcionou para mim no Solaris removendo -To mvcomando.
21712 Ham

261

Menor ainda gosto bastante:

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

Em sistemas de arquivos que não diferenciam maiúsculas de minúsculas, como o HFS + do OS X , você deseja adicionar o -fsinalizador:

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

3
linux.icydog.net/rename.php :The renaming utility that comes by default with Ubuntu is a Perl program sometimes called prename
sleepsort

1
Obrigado, usei-o desta maneira $ find | xargs renomeia 'y / AZ / az /' *
Rashi

5
Isso pressupõe que você renomeie perls, o que nem sempre é o caso. por exemplo, no meu debian renomear é completamente diferente
Krzysztof Krasoń 17/03

10
No Arch, o programa é chamado perl-rename(e é fornecido por um pacote com o mesmo nome)
jaymmer - Reinstate Monica

3
Isso não responde ao problema fornecido, pois "renomear" não é recursivo.
Cybolic 19/09/16

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

Por favor, faça um "mkdir -p A / B / C" antes de executar seu script.
tzot 30/09/08

2
"find" deve ser substituído por qualquer comando usado para obter uma lista dos arquivos que você deseja renomear; por exemplo, "ls *. {C, CPP, H}".
JPaget

2
Não funciona no OS X, apenas imprime as informações de uso para find. find .parece consertar isso.
emlai

3
A versão "consolidada", de todos os comentários (a resposta não pode mais ser editada):for f in `find .`; do mv -v "$f" "`echo $f | tr '[A-Z]' '[a-z]'`"; done
romaricdrigon

1
Alguém pode me explicar por que isso deve funcionar em primeiro lugar? Se encontrar ./FOO e ./FOO/BAR, primeiro renomeia ./FOO para ./foo, após o que não poderá mais encontrar ./FOO/BAR. Inserir minha própria resposta abaixo que deve corrigir isso.
oisyn

80

Simplesmente tente o seguinte se não precisar se preocupar com eficiência.

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

13
Uau, essa é uma maneira bastante ineficiente.
precisa saber é o seguinte

28
Supondo que a eficiência computacional normalmente NÃO seja uma preocupação quando você precisar renomear arquivos em um diretório ... Eu acho que essa é provavelmente a solução mais criativa e simples nesse segmento. Realmente, algo tão trivial não deve ser tão complicado quanto as respostas aceitas demonstram e essa solução alternativa é muito mais proporcional à quantidade de pensamento que desejo investir em uma operação como essa. +1000
Peter M. Elias

27
Você pode acelerar com o -0interruptor (que é 'zero', sem compactação) zip -0 -r foo.zip foo/,.
Johnny Baloney

3
Eu tive que mudar mais de 50.000 nomes de arquivos em diferentes estruturas de diretório. A resposta original aceita quase 10% do trabalho em 2 minutos, onde isso, com a -0compressão, foi quase instantâneo!
Peon

2
Isso não é apenas ineficiente. Este é simplesmente impossível se não houver espaço livre suficiente para um arquivo temporário.
Victor Yarema 5/09/19

19

Cara, vocês / moças gostam de complicar demais as coisas ...

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

2
Isso não funciona, ele diz que o arquivo com nome em minúsculas já existe.
22417 kirhgoff

@kirhgoff Já vi isso acontecer com montagens NFS - siga as instruções aqui .
precisa saber é o seguinte

Como podemos renomear em um sistema de arquivos com distinção entre maiúsculas e minúsculas? rename 'y/a-z/A-Z/' /tmp/*.jpg-> Can't rename /tmp/*.jpg /TMP/*.JPG: No such file or directory, ou seja, tente renomear o caminho inteiro em vez de apenas o arquivo.
Pablo A

14

A maioria das respostas acima é perigosa porque não lida com nomes contendo caracteres estranhos. Sua aposta mais segura para esse tipo de coisa é usar finda -print0opção '' , que encerrará os nomes de arquivos com ASCII NUL em vez de \ n.

Aqui está um script, que apenas altera arquivos e não nomes de diretório, para não confundir find:

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 contêm espaços, todos os tipos de aspas, etc. Isso é importante porque se você executar, como root, um desses outros scripts em uma árvore que inclua o arquivo criado por

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

... bem, adivinhe ...


Seria mais simples de usar em renamevez de mv.
Victor Yarema

14

Isso funciona se você já possui ou configurou o comando renomear (por exemplo, através da instalação do brew no Mac):

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

9
... se você configurar renomeação primeiro, por exemplo, atravésbrew install rename
pduersteler

3
renomear 'y / AZ / az /' *
Stephen

4
--lower-case? Infelizmente, esse não é o caso do Arch Linux.

3
renamenão é um bash embutido. É um utilitário externo e a sintaxe será diferente dependendo da versão que você instalou. Esta resposta não é suficiente como uma solução portátil "para todos os sistemas operacionais baseados em unix", conforme reivindicado.
Corey Goldberg #

2
Apenas verificado, o renamecomando incluído no último Fedora (28), através do util-linux-2.31pacote, não tem a --lower-caseopção.
NiXar 09/0918

11

Isso funciona no CentOS / Red Hat Linux ou em outras distribuições sem o renamescript Perl:

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

Origem: renomeie todos os nomes de arquivo de caracteres maiúsculos para minúsculos

(Em algumas distribuições, o renamecomando padrão vem do util-linux, e essa é uma ferramenta diferente e incompatível.)


Isso só funciona em um único diretório, não recursivamente
Bram

Coool .. Funciona bem
Manikandan Ram 18/03

6

A abordagem mais simples que encontrei no Mac OS X foi usar o pacote de renomeação em http://plasmasturm.org/code/rename/ :

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

--force Renomeie mesmo quando um arquivo com o nome de destino já exista.

--long-case - Converte nomes de arquivos em minúsculas.

--nows Substitua todas as seqüências de espaço em branco no nome do arquivo por caracteres sublinhados únicos.


5

Aqui está minha solução abaixo do ideal, usando um script de 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

Todas as pastas são renomeadas corretamente e mvnão fazem perguntas quando as permissões não coincidem, e as pastas CVS não são renomeadas (os arquivos de controle CVS nessa pasta ainda são renomeados, infelizmente).

Como "find-profundidade" e "find | sort -r" retornam a lista de pastas em uma ordem utilizável para renomear, eu preferi usar "-profundidade" para pesquisar pastas.


Sua primeira descoberta não funciona. Tente "mkdir -p A / B / C" e depois execute seu script.
tzot 30/09/08

4

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

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

[edit] Eu tentei isso, e incorporar a tradução em letras minúsculas dentro da descoberta não funcionou por razões que eu realmente não entendo. Portanto, altere isso para:

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

Ian


4

Usando o fixador de nome de arquivo 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 quanto

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

(onde fixé claro o script acima)


3

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

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 -thpth na primeira localização.


1
A única coisa que funcionou em um Mac a partir de todo o segmento. Obrigado montes!
YemSalat

2

Não é portátil, apenas Zsh, mas bastante conciso.

Primeiro, verifique se zmvestá carregado.

autoload -U zmv

Além disso, verifique se extendedglobestá ativado:

setopt extendedglob

Então use:

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

Arquivos e diretórios em letras minúsculas recursivamente em que o nome não é CVS .


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

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


Isso não funciona: ele renomeia um diretório antes de operar no conteúdo, de forma que a tentativa posterior do conteúdo falhe.
ameixa

Adicionando -depthcorreções! Esta é uma solução muito rápido, mas é claro sem utilizar a -print0opção de encontrar, não é o mais confiável
jpaugh

@jpaugh Não, não. Ele tentará renomear ./FOO/BAR para ./foo/bar, mas ./foo ainda não existe.
oisyn

2

Com o MacOS,

Instale o renamepacote,

brew install rename

Usar,

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

Este comando encontra todos os arquivos com uma *.pyextensão 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

Por que você usou a -Iopção xargsaqui?
Victor Yarema 5/09/19

A - Ibandeira não é obrigatória. É usado quando executamos vários comandos com xargs. Aqui está um exemplo de como usar vários comandos xargs cat foo.txt | xargs -I % sh -c 'echo %; mkdir %' Você pode executar sem -Igostofind . -iname "*.py" -type f | xargs -I% rename -c -f "%"
raj akilesh

2

Use typeset :

typeset -l new        # Always lowercase
find $topPoint |      # Not using xargs to make this more readable
  while read old
  do new="$old"       # $new is a lowercase version of $old
     mv "$old" "$new" # Quotes for those annoying embedded spaces
  done

No Windows, emulações, como Git Bash , podem falhar porque o Windows não diferencia maiúsculas de minúsculas. Para aqueles, adicione uma etapa que seja mvo arquivo com outro nome primeiro, como "$ old.tmp" e depois para $new.


1

Longo, mas "funciona sem surpresas e sem instalações"

Esse script lida com nomes de arquivos com espaços, aspas, outros caracteres incomuns e Unicode, funciona em sistemas de arquivos que não diferenciam maiúsculas de minúsculas e na maioria dos ambientes Unix-y que possuem bash e awk instalados (ou seja, quase todos). Ele também relata colisões, se houver (deixando o nome do arquivo em maiúsculas) e, é claro, renomeia os arquivos e diretórios e funciona recursivamente. Finalmente, é altamente adaptável: você pode ajustar o comando find para direcionar os arquivos / diretórios desejados e pode alterar o awk para fazer outras manipulações de nomes. Observe que, por "manipula Unicode", quero dizer que ele realmente converterá o caso deles (não os ignorará como respostas usadas 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 excelentes respostas:

/unix/9496/looping-through-files-with-spaces-in-the-names

Como converter uma string para minúsculas no Bash?


1

Isso também funciona bem no macOS:

ruby -e "Dir['*'].each { |p| File.rename(p, p.downcase) }"

Isso é especialmente interessante, pois o Ruby é enviado com o macOS.
Jxmorris12

1

Eu precisava fazer isso em uma configuração do Cygwin no Windows 7 e descobri que havia 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 diretamente dos fóruns do Ubuntu funcionou fora da lata :-)

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

(NB: eu substituí anteriormente espaços em branco por sublinhados usando:

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

)


1

No OS X, mv -fmostra o erro "mesmo arquivo", então renomeio 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

1

Eu procuraria o Python nessa situação, para evitar a otimização de caminhos sem espaços ou barras. Eu também descobri que python2tende 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])

Falar sobre tornando-se complicado ... apenas fazer .. renomeação 'y / AZ / az /' *
Stephen

1
@ Stephen, que assume que você tem acesso de administrador para instalar a renomeação. Frequentemente, preciso coletar dados em sistemas nos quais não tenho a capacidade de instalar outro software. No Ubuntus recente, isso é instalado apenas se o perl estiver presente.
John Foley

1

Se você usa o Arch Linux , pode instalar o renamepacote) a partir do AURqual fornece o renamexmcomando como /usr/bin/renamexmexecutável e uma página de manual junto com ele.

É uma ferramenta realmente poderosa para renomear rapidamente arquivos e diretórios.

Converter em minúsculas

rename -l Developers.mp3 # or --lowcase

Converter em maiúsculas e minú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 # Substitute string on pattern

--yes # Confirm all actions

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


1
renomear a partir brewdo macOS está -lvinculado à criação de um link simbólico e -cà conversão para minúsculas, então cuidado.
precisa saber é o seguinte

0
( 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, renomeie os diretórios de baixo para cima, classifique -r (onde -depth não está disponível), depois os arquivos. Então grep -v / CVS em vez de find ...- podar porque é mais simples. Para diretórios grandes, para f in ... pode estourar alguns buffers de shell. Use find ... | enquanto lê para evitar isso.

E sim, isso estraga arquivos que diferem apenas no caso de ...


Primeiro: find YOURDIR -type d | sort -ré demais. Você quer find YOURDIR -depth -type d. Segundo, o find -type fDEVE executar após os diretórios terem sido renomeados.
tzot 30/09/08

0
find . -depth -name '*[A-Z]*'|sed -n 's/\(.*\/\)\(.*\)/mv -n -v -T \1\2 \1\L\2/p'|sh

Não tentei os scripts mais elaborados mencionados aqui, mas nenhuma das versões de linha de comando funcionou para mim no meu Synology NAS. renamenão está disponível e muitas das variações de findfalha porque parece aderir ao nome antigo do caminho já renomeado (por exemplo, se encontrar ./FOOseguido por ./FOO/BAR, renomear ./FOOpara ./foocontinuará a ser listado, ./FOO/BARmesmo que esse caminho não seja mais válido) . O comando acima funcionou para mim sem problemas.

A seguir, é apresentada uma explicação de cada parte do comando:


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

Ele encontrará qualquer arquivo do diretório atual (mude .para o diretório que você deseja processar), usando uma pesquisa aprofundada (por exemplo, listará ./foo/barantes ./foo), mas apenas para arquivos que contenham caracteres maiúsculos. O -namefiltro se aplica apenas ao nome do arquivo base, não ao caminho completo. Portanto, isso listará, ./FOO/BARmas não ./FOO/bar. Tudo bem, pois não queremos renomear ./FOO/bar. ./FOOPorém, queremos renomear , mas esse será listado mais tarde (é por isso que -depthé importante).

Esse comando por si só é particularmente útil para localizar os arquivos que você deseja renomear em primeiro lugar. Use isso após o comando rename completo para procurar arquivos que ainda não foram substituídos devido a erros ou colisões de nomes de arquivos.


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

Esta parte lê os arquivos gerados por finde os formata em um mvcomando usando uma expressão regular. A -nopção para sedde imprimir a entrada e o pcomando na regex de pesquisa e substituição gera 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 do arquivo. O diretório é deixado intacto, mas o nome do arquivo é transformado em minúsculas. Então, se findsaídas ./FOO/BAR, ele se tornará mv -n -v -T ./FOO/BAR ./FOO/bar. A -nopção de mvgarantir que os arquivos em minúsculas existentes não sejam substituídos. A -vopção produz a mvsaída a cada alteração que faz (ou não faz - se ./FOO/barjá existe, produz algo como ./FOO/BAR -> ./FOO/BAR, observando que nenhuma alteração foi feita). O -Té muito importante aqui - trata o arquivo de destino como um diretório. Isso garantirá que ./FOO/BARnão seja movido para ./FOO/baro diretório existente.

Use isso junto com findpara gerar uma lista de comandos que serão executados (útil para verificar o que será feito sem realmente fazê-lo)


sh

Isso é bastante auto-explicativo. Ele roteia todos os mvcomandos gerados para o interpretador de shell. Você pode substituí-lo por bashqualquer concha que desejar .


0

Renomear Slugify (regex)

Não é exatamente o que o OP solicitou, mas o que eu esperava encontrar nesta página:

Uma versão "slugify" para renomear arquivos para que sejam semelhantes aos URLs (ou seja, incluem apenas alfanuméricos, pontos e traços):

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

2
"não exatamente"? isso não é nada do que o OP pediu #
Corey Goldberg
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.