Você será executado em alguns problemas se quiser renomear os diretórios e ao mesmo tempo. Renomear apenas um arquivo é bastante fácil. Mas você quer ter certeza de que os diretórios também sejam renomeados. Você não pode simplesmente mv Motörhead/Encöding Motorhead/Encoding
, pois Motorhead
não existirá no momento da chamada.
Portanto, precisamos de uma profundidade primeiro de todos os arquivos e pastas e, em seguida, renomeie apenas o arquivo ou a pasta atual. O seguinte funciona com GNU find
e Bash 4.2.42 no meu OS X:
#!/usr/bin/env bash
find "$1" -depth -print0 | while IFS= read -r -d '' file; do
d="$( dirname "$file" )"
f="$( basename "$file" )"
new="${f//[^a-zA-Z0-9\/\._\-]/}"
if [ "$f" != "$new" ] # if equal, name is already clean, so leave alone
then
if [ -e "$d/$new" ]
then
echo "Notice: \"$new\" and \"$f\" both exist in "$d":"
ls -ld "$d/$new" "$d/$f"
else
echo mv "$file" "$d/$new" # remove "echo" to actually rename things
fi
fi
done
Salve este script como rename.sh
, torne-o executável com chmod +x rename.sh
. Em seguida, chame como rename.sh /some/path
. Resolva quaisquer colisões de nomes de arquivos (anúncios " Notice
").
Se você estiver absolutamente certo de que ele faz as substituições corretas, remova o echo
do script para realmente renomear as coisas, em vez de simplesmente imprimir o que ele faz.
Por segurança, eu recomendaria testar isso em um pequeno subconjunto de arquivos primeiro.
Opções explicadas
Para explicar o que acontece aqui:
-
-depth
garantirá que os diretórios sejam recursivos em profundidade primeiro, para que possamos "acumular" tudo desde o final. Normalmente,find
atravessa de forma diferente (mas não em largura). -
-print0
garante que a saídafind
seja delimitada por nulo, portanto, podemos lê-la comread -d ''
na variávelfile
. Isso nos ajuda a lidar com todos os tipos de nomes de arquivos estranhos, incluindo aqueles com espaços e até mesmo novas linhas. - Obteremos o diretório do arquivo com
dirname
. Não se esqueça de sempre citar suas variáveis corretamente, caso contrário, qualquer caminho com espaços ou caracteres globbing quebraria esse script. - Obteremos o nome do arquivo real (ou nome do diretório) com
basename
. - Em seguida, removemos qualquer caractere inválido de
$f
usando os recursos de substituição de string do Bash. Inválido significa qualquer coisa que não seja uma letra maiúscula ou minúscula, um dígito, uma barra (\/
), um ponto (\.
), um sublinhado ou um hífen negativo. - Se
$f
já estiver limpo (o nome limpo é idêntico ao nome atual), pule-o. - Se o
$new
já existir no diretório$d
(por exemplo, você tem arquivos com o nomeresume
erésumé
no mesmo diretório), emita um aviso. Você não quer renomeá-lo, porque, em alguns sistemas,mv foo foo
causa um problema. Caso contrário, - Finalmente renomeamos o arquivo original (ou diretório) para o novo nome
Como isso só funcionará na hierarquia mais profunda, renomear Motörhead/Encöding
para Motorhead/Encoding
será feito em duas etapas:
-
mv Motörhead/Encöding Motörhead/Encoding
-
mv Motörhead Motorhead
Isso garante que todas as substituições sejam feitas na ordem correta.
Arquivos de exemplo e execução de teste
Vamos supor que alguns arquivos em uma pasta base sejam chamados de test
:
test
test/Motörhead
test/Motörhead/anöther_file.mp3
test/Motörhead/Encöding
test/Randöm
test/Täst
test/Täst/Töst
test/with space
test/with-hyphen.txt
test/work
test/work/resume
test/work/résumé
test/work/schedule
Aqui está a saída de uma execução no modo de depuração (com o echo
na frente do mv
),
ou seja, os comandos que seriam chamados e os avisos de colisão:
mv test/Motörhead/anöther_file.mp3 test/Motörhead/another_file.mp3
mv test/Motörhead/Encöding test/Motörhead/Encoding
mv test/Motörhead test/Motorhead
mv test/Randöm test/Random
mv test/Täst/Töst test/Täst/Tost
mv test/Täst test/Tast
mv test/with space test/withspace
Notice: "resume" and "résumé" both exist in test/work:
-rw-r—r-- … … test/work/resume
-rw-r—r-- … … test/work/résumé
Observe a ausência de mensagens para with-hyphen.txt
, schedule
e test
em si.