substitua todos os pontos em nomes de arquivos por sublinhado

5

Eu tenho um diretório com alguns subdiretórios com nomes de arquivos e nomes de diretórios que contêm alguns pontos:

$ ls -R
.:
dir  file.with.dots

./dir:
subdir.with.dots

./dir/subdir.with.dots:
other.file

Eu realmente luto para pegar todos esses arquivos com find porque eu não quero renomear a pasta atual . e .. .

Eu também li como Substituir pontos por sublinhados em nomes de arquivos, deixando a extensão intacta , mas isso não se encaixa aqui, porque o principal problema é a seleção recursiva dos arquivos.

Eu tentei substituir os pontos por find e tr :

find $(pwd) -depth -name '*.*'|while read f; do mv -iv "$f" '"'$(echo $f|tr '.' '_')'"' ; done

mas isso daria erros se em ambos, o caminho para o arquivo e o próprio arquivo contiverem pontos

Como renomeio e substituo todas as ocorrências de um ponto por um sublinhado?

    
por rubo77 10.08.2014 / 22:24

1 resposta

10

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 '%s
autoload 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-print0' {} + se sua pesquisa for compatível).

Você precisa:

  • -print0 / -exec printf '%s-d ''' {} + , 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
  • 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 de read para -r , caso contrário, retira-os do final do registro de entrada).
  • read senão mv trata 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 -i seja o terminal (para as respostas aos prompts find ), não o pipe de %code% .
por 10.08.2014 / 22:39