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

2

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
    
por Rafael 15.05.2018 / 22:52

4 respostas

3

Use a localização 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 e subdiretórios atuais.

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:]]/"_"}" - caminho / novo_nome_de_arquivo

    
por 16.05.2018 / 03:01
3

Use rename utility em vez disso, por exemplo,

rename "s/ /_/g" *

Perl-powered file rename script with many helpful built-ins.

Para renomeação recursiva, tente:

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

em que ** é uma opção de globalização do Bash (ativar por shopt -s globstar ).

Como alternativa, use find , por exemplo,

find . -type f -execdir rename "s/ /_/g" {} ';'
    
por 16.05.2018 / 00:29
0

Em bash (não tenho certeza sobre derivativos e sh !) você pode usar substituição de variáveis etc em variáveis

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

NO ENTANTO, este atleast não trata de outros caracteres de espaço (aba, etc)

    
por 15.05.2018 / 23:00
0

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 fornecido ( bash ) em um diretório no qual o objeto está, portanto, qualquer caminho passado por {} é sempre como ./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ções (você pode omiti-lo se quiser).
  • bash nos permite usar a sintaxe "${baz// /_}" .
  • -depth garante que o seguinte não acontecerá: find renomeia um diretório (se aplicável) e, em seguida, tenta processar seu conteúdo pelo caminho antigo .
  • {} + é capaz de alimentar bash com objetos múltiplos (ao contrário da sintaxe {} \; ). Nós iteramos sobre eles com for f in "$@" . O objetivo não é executar um processo bash separado para cada objeto, pois a criação de um novo processo é dispendiosa. Acho que não podemos evitar facilmente executar mv -s; Ainda assim, reduzir o número de invocações bash parece uma boa otimização ( pwd é embutido em bash e não nos custa um processo). No entanto, -execdir ... {} + não passará arquivos de diretórios diferentes juntos. Ao usar -exec ... {} + em vez de -execdir ... {} + , podemos reduzir ainda mais o número de processos, mas precisamos nos preocupar com os caminhos, não apenas os nomes de arquivos (compare esta outra resposta , parece fazer um trabalho decente, mas while read reduz a velocidade ). Esta é uma questão de velocidade versus simplicidade (relativa). Minha solução com -exec está abaixo.
  • dummy pouco antes de {} se tornar $0 dentro de nosso bash . Precisamos desse argumento fictício porque "$@" é equivalente a "$1" "$2" ... (não "$0" "$1" ... ). Dessa forma, tudo o que passou por {} está disponível posteriormente como "$@" .

Versão mais complexa e ligeiramente otimizada (vários ${...} truques extraídos 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 , o qual pode não ser um editor interativo :

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

Advertências:

  • 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 de base), portanto, renomear arquivos apenas (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 no caso geral. Eu o incluí na minha resposta porque achei essa abordagem experimental interessante.

    
por 23.05.2018 / 12:08