Tenho vários diretórios dentro de um diretório que preciso renomear para incluir um caractere de sublinhado "_" no início do nome.
Como teste, criei um diretório pai cujo conteúdo possui três diretórios:
dir1, dir2 and dir3; three files: file1, file2, file3
e três arquivos com prefixo de ponto:
.file1, .file2, .file3
Até agora, tentei usar gfind
, depois de instalar findutils
no Mac:
gfind . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
Isso retorna:
mv: rename ./. to ./_.: Invalid argument
Então, tentei usar renomear com find:
find . -type d -exec rename 's/^/_/' {} \;
Isso retorna:
Can't rename '.' to '_.': Invalid argument
Can't rename './dir1' to '_./dir1': No such file or directory
Can't rename './dir2' to '_./dir2': No such file or directory
Can't rename './dir3' to '_./dir3': No such file or directory
Alguém tem uma solução viável ou sabe onde posso estar?
Responder1
Se entendi direito, você tem arquivos e diretórios como este:
$ mkdir dir{1,2,3} ; touch file{1,2,3} .file{1,2,3}
$ ls -A
.file1 .file2 .file3 dir1 dir2 dir3 file1 file2 file3
e deseja renomear os diretórios para _dir1
, _dir2
, _dir3
?
Isso find
/ printf mv
parece factível. Vamos ver o que ele imprime:
$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n"
mv "./." "./_."
mv "./dir2" "./_dir2"
mv "./dir3" "./_dir3"
mv "./dir1" "./_dir1"
Esse primeiro comando gerará um erro, pois o “ponto” é especial e você não pode renomeá-lo. Mas os outros devem estar ok, e devem renomear os diretórios como desejar:
$ find . -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
mv: cannot move ‘./.’ to ‘./_.’: Device or resource busy
$ ls -d _dir*
_dir1 _dir2 _dir3
Mas canalizar comandos para um shell é um pouco feio, e se os nomes dos arquivos forem estranhos o suficiente, os resultados serão surpreendentes (por exemplo, se eles contiverem $
sinais que acionarão a substituição de variáveis ou comandos no shell).
Se os arquivos estiverem todos em um nível, isso deverá servir:
for x in * ; do [ -d "$x" ] && mv "$x" "_$x" ; done
(Embora se _$x
já existir, $x
será movido para ele.)
Se você quiser incluir diretórios cujos nomes começam com um ponto, use shopt -s dotglob
antecipadamente.
Isso também está próximo:
find . -type d -exec rename 's/^/_/' {} \;
Mas como find
fornece rename
os caminhos que começam com ./
, precisamos levar isso em consideração. Em apenas um nível, isso deve servir (muda o início ./
para ./_
):
find . -type d -exec rename 's,^./,./_,' {} \;
Para obter os diretórios em todos os níveis, find -execdir
pode ser mais fácil de usar. Ele executa o comando no diretório do arquivo. Precisamos -depth
lidar com as renomeações na ordem correta.
find . -depth -type d -execdir rename 's,^./,./_,' {} \;
Talvez adicione ! -name .
também. por exemplo
$ mkdir foo foo/dir1 foo/dir2
$ find . -depth \! -name . -type d -execdir rename 's,^./,./_,' {} \;
$ ls _foo
_dir1 _dir2
Responder2
O problema com sua primeira tentativa é que você está tentando renomear .
a si mesmo (o diretório atual), além de todos os subdiretórios. Pule isso.
gfind . -mindepth 1 -type d -name '*' -printf "mv \"%h/%f\" \"%h/_%f\"\n" | sh
Mas na verdade, não faça isso. nunca coloque coisas em sh
.Isso não funcionaa menos que seus nomes de arquivo não contenham nenhum caractere especial do shell. Se você tiver um arquivo chamado `rm -r ~`
(um nome de arquivo perfeitamente válido, embora incomum), então pop vai para seu diretório inicial. Invoque um shell -exec
como mostro abaixo.
Sua segunda tentativa não funciona porque você está inserindo _
no início do caminho completo, em vez de adicioná-lo no início do nome base, ou seja, após o último /
.
find . -mindepth 1 -type d -exec rename 's![^/]+$!_$&!' {} \;
Nenhum deles realmente funciona se você tiver diretórios aninhados, porque eles renomeiam os diretórios enquanto find
os percorrem. Ao fazer isso, você precisa agir no conteúdo de um diretório antes de agir no próprio diretório, com a -depth
opção.
find . -mindepth 1 -depth -type d -exec rename 's![^/]+$!_$&!' {} \;
De forma mais portável, chame um shell e execute mv
. Isso funciona em qualquer sistema POSIX.
find . -depth -type d -name . -o -exec sh -c '
for d do mv "$d" "${d%/*}/_${d##*/}"; done
' sh {} +
Se não houver subdiretórios aninhados, você poderá usar um loop de shell simples. Se você não quiser renomear diretórios cujo nome comece com um ponto, basta usar o */
curinga, que corresponde apenas a diretórios e links simbólicos para diretórios.
for d in */; do
if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi # robustness in case there are no subdirectories
if [ -L "$d" ]; then continue; fi # optional: skip symbolic links to directories
mv -- "$d" "_${d%/}"
done
Se você quiser renomear todos os diretórios, incluindo aqueles cujo nome começa com um ponto, inclua .*
também o curinga.
for d in .*/ */; do
if [ "$d" = "*/* ] && [ ! -e "$d" ]; then continue; fi
if [ -L "$d" ]; then continue; fi
mv -- "$d" "_${d%/}"
done
Observe que quando você estiver migrando foo
para _foo
, se já existir um diretório chamado _foo
, isso moverá o antigo foo
para _foo/foo
. Para se proteger contra isso, adicione um teste explícito.
Ou, alternativamente, use zsh. Está pré-instalado no macOS. Primeiro execute autoload -U zmv
(coloque isso no seu .zshrc
ou no seu script) para carregar ozmv
função, então a versão não recursiva (incluindo arquivos de ponto, excluindo links simbólicos - (/D)
é um conjunto deeliminatórias globais):
zmv -Q '*(/D)' '_$f'
ou a versão recursiva (usandomodificadorespara extrair o dirname e o basename):
zmv -Q '**/*(/D)' '$f:h/_$f:t'
zmv
se recusará a fazer um movimento se o alvo já existir.