com zsh :
autoload zmv
zmv '(**/)(*.*)' '$1${2//./_}'
Caso contrário, se você tiver acesso a bash (embora ksh93 ou zsh também funcione), você sempre poderá fazer:
find . -depth ! -name '.*' -name '*.*' -exec bash -c '
for file do
dir=${file%/*}
base=${file##*/}
mv -i -- "$file" "$dir/${base//./_}"
done' sh {} +
(embora você perca as verificações de integridade feitas por zmv ).
Aqueles (intencionalmente) não renomear arquivos ocultos.
O ponto é processar a lista em profundidade primeiro (que zmv faz por padrão e usando -depth em find ) e apenas renomear o nome base do arquivo.
Para que você faça:
mv -- a.b/c.d a.b/c_d
e então :
mv -- a.b a_b
Observe também que, para a solução não-zmv, se você tiver um arquivo a.b e a_b no mesmo diretório, o mv -- a.b a_b ficará feliz em mover a.b para %código%. Para evitar isso, se estiver em um sistema GNU, você pode adicionar a opção a_b/ a -T .
Como eu vejo, você tentou editar a resposta para usar um mv loop.
Observe que você geralmente deve evitar while read loops, mesmo neste caso em que um comando por linha deve ser executado de qualquer maneira. Se você realmente quer usar um, então fazer certo é complicado e envolve ksh / bash / zshisms:
{
find . -depth ! -name '.*' -name '*.*' -exec printf '%sautoload zmv
zmv '(**/)(*.*)' '$1${2//./_}'
' {} + |
while IFS= read -rd '' file; do
dir=${file%/*}
base=${file##*/}
mv -i -- "$file" "$dir/${base//./_}"
done <&3 3<&-
} 3<&0
(você pode substituir while read por -exec printf '%s se sua pesquisa for compatível). -print0' {} +
Você precisa:
-
-print0/-exec printf '%s, já que você não pode confiar em nova linha como o separador, já que nova linha é um caractere válido em um nome de arquivo-d ''' {} + - como resultado, você precisa de
read(IFS=lê até NUL em vez de NL) -
IFS(isto é, remover todos os caracteres de espaço em branco dereadpara-r, caso contrário, retira-os do final do registro de entrada). -
readsenãomvtrata a barra invertida como um caractere de escape (para o campo e separador de registro). -
essa pequena dança com descritores de arquivo porque você quer que a entrada padrão de
-iseja o terminal (para as respostas aos promptsfind), não o pipe de %code% .