Questão Como substituir espaços em branco com sublinhado em todos os nomes de arquivos?


O script fornecido abaixo coloca "sublinhado" em vez de "espaço em branco" em todos os nomes de arquivos que estão em uma determinada pasta. Estou tendo problemas para criar um script de shell que coloque "sublinhado" em vez de "espaço em branco" nos nomes de todas as subpastas e arquivos contidos neles e não apenas em uma pasta.

Alguém tem alguma dica de como posso fazer isso?

Aqui está o meu código:

#!/bin/bash
ls | while read -r FILE; do
  mv -v "$FILE" `echo $FILE | tr ' ' '_'`
done

2


origem


Canalizar a saída de find dentro de voce while loop em vez de ls. Seria mais seguro ler o manual e usar opções como -maxdepth. Você também pode olhar para o -exec opção para evitar o loop completamente. - David
Por que você não deve analisar a saída de ls (1). - Kamil Maciorowski


Respostas:


Use find para pesquisar em diretórios e subdiretórios:

while IFS='' read -r -d '' fname ; do
   nname="${fname##*/}"
   mv -v -n "${fname}"  "${fname%/*}/${nname//[[:space:]]/_}"
done < <(find "$(pwd)"  -name "* *" -type f  -print0)

find "$(pwd)" -type f -print0 - Imprime todos os caminhos de arquivos encontrados no diretório atual e subdiretórios.

Com a saída de substituição de processo do comando find é enviada para loop while, onde lê a variável fname.

nname="${fname##*/}" - Extrai o nome do arquivo do caminho

"${fname%/*}" - extrai o caminho

"${nname//[[:space:]]/"_"}" - substitui espaços no nome do arquivo por _

"${fname%/*}/${nname//[[:space:]]/"_"}" - path / new_filename


3



Você precisa remover as aspas duplas ao redor do sublinhado; já que ele já está entre aspas duplas, eles aparentemente são tratados como caracteres literais e acabam no nome do arquivo. Além disso, recomendo adicionar aspas duplas $(pwd) caso contenha espaços (ou apenas use . em vez disso) e adicione -name "* *" ao find comando para pular arquivos que não possuem espaços e adicionar -i ou -n ao mv comando para evitar a perda de dados se houver um conflito de nome de arquivo. - Gordon Davisson
Bons pontos. Aspas duplas dentro de aspas duplas funcionam no meu shell, mas vou mudar isso também. Obrigado.
Interessante. Aparentemente, o tratamento de aspas duplas nesse contexto mudou em algum momento entre o bash v4.2 e o bash v4.4. Eu estava testando com o bash v3; você provavelmente tem uma versão mais nova. - Gordon Davisson


Usar rename utilitário em vez disso, por ex.

rename "s/ /_/g" *

Script de renomeação de arquivos com suporte a Perl com muitos recursos úteis incorporados.


Para renomeação recursiva, tente:

rename "s/ /_/g" **/*.*

Onde ** é um Opção de globulação de bash (habilitar por shopt -s globstar).

Como alternativa, use find, por exemplo.

find . -type f -execdir rename "s/ /_/g" {} ';'

3





Dentro bash (não tenho certeza sobre derivados e sh!) você pode usar substituição variável etc em variáveis

FILE="file name"
touch "$FILE"
mv -v "$FILE" "${FILE// /_/}"

No entanto, este atleast não lida com outros caracteres de espaço (aba, etc)


0



Obrigado Wilf! Ainda não posso. Em breve verei sua sugestão com mais cuidado. O script que eu coloquei acima insere "sublinhado" entre todas as palavras que possuem "espaços em branco". Eu gostaria que a mesma coisa fosse feita no nome dos arquivos que estão em subpastas - Rafael


Minha abordagem:

find . -depth -name "* *" -execdir bash -c 'pwd; for f in "$@"; do mv -nv "$f" "${f// /_}"; done' dummy {} +

Versão multilinha para legibilidade:

find . -depth -name "* *" -execdir \
   bash -c '
      pwd
      for f in "$@"; do
          mv -nv "$f" "${f// /_}"
      done
   ' dummy {} +

Explicação:

  • find . -name "* *" encontra objetos que precisam ser renomeados. Nota find é muito flexível com seus testes, portanto, se você quiser (por exemplo) renomear apenas os diretórios, comece com find . -depth -type d -name "* *".
  • -execdir executa o processo dado (bash) em um diretório onde o objeto está, então qualquer caminho passado por {} é sempre assim ./bar, não ./foo/bar. Isso significa que não precisamos nos preocupar com todo o caminho. A desvantagem é mv -v não mostrará o caminho, então eu adicionei pwd apenas para informação (você pode omitir se quiser).
  • bash nos permite usar o "${baz// /_}" sintaxe.
  • -depth garante que o seguinte não irá acontecer: find renomeia um diretório (se aplicável) e, em seguida, tenta processar seu conteúdo velho caminho.
  • {} + é capaz de alimentar bash com múltiplo objetos (ao contrário de {} \; sintaxe). Nós iteramos sobre eles com for f in "$@". O ponto não é para executar um separado bash processo para cada objeto desde a criação de um novo processo é caro. Eu acho que não podemos evitar facilmente correr separados mv-s; ainda, reduzindo o número de bash invocações parece uma boa otimização (pwd é um embutido bash e não nos custa um processo). Contudo -execdir ... {} + não passará arquivos de diretórios diferentes juntos. Usando -exec ... {} + ao invés de -execdir ... {} + podemos reduzir ainda mais o número de processos, mas precisamos nos preocupar com os caminhos, não apenas com nomes de arquivos (comparar esta outra resposta, parece fazer um trabalho decente, mas while read diminui a velocidade). Esta é uma questão de velocidade versus simplicidade (relativa). Minha solução com -exec está abaixo.
  • dummypouco antes {} torna-se $0 dentro do nosso bash. Precisamos desse argumento falso porque "$@" é equivalente a "$1" "$2" ... (não "$0" "$1" ...). Assim tudo passou por {} está disponível mais tarde "$@".

Versão mais complexa e ligeiramente otimizada (várias ${...} truques tirados de outra resposta):

find . -depth -name "* *" -exec \
   bash -c '
      for f in "$@"; do
          n="${f##*/}"
          mv -nv "$f" "${f%/*}/${n// /_}"
      done
   ' dummy {} +

Outra abordagem (experimental!) Envolve vidir. O truque é vidir usa $EDITOR qual pode não ser um editor interativo:

find . -name "* *" | EDITOR='sed -i s/\d32/_/g' vidir -

Ressalvas:

  • Isso falhará nos nomes de arquivos / diretórios com caracteres especiais (por exemplo, novas linhas).
  • Não podemos usar s/ /_/g diretamente, \d32 é uma solução alternativa.
  • Por causa de como vidir funciona, a abordagem seria complicada se você quiser substituir um dígito ou uma guia.
  • Aqui vidir funciona com caminhos, não apenas nomes de arquivos (nomes base), assim, renomear apenas arquivos (ou seja, não diretórios) pode ser difícil.

No entanto, se você sabe o que está fazendo, isso pode ser ainda mais rápido. Eu não recomendo tal (ab) uso de vidir em geral caso embora. Eu o incluí na minha resposta porque achei essa abordagem experimental interessante.


0