Como substituir espaços em todos os nomes de arquivos por sublinhado no Linux usando o shell script?

16

Eu tentei seguir o shell script que deve substituir os espaços de todos os nomes de arquivos xml

for xml_file in $(find $1 -name "* .xml" -type f);
do
 echo "removing spaces from XML file:" $xml_file
 mv "$xml_file" "${xml_file// /_}";
done

Suponha, eu tenho um arquivo xml com o nome xy z.xml, e ele fornece:

removing spaces from XML file: /home/krishna/test/xy
mv: cannot stat `/home/krishna/test/xy': No such file or directory
removing spaces from XML file: .xml
mv: cannot stat `z.xml': No such file or directory
Krishna
fonte

Respostas:

26

Use isso com bash:

find $1 -name "* *.xml" -type f -print0 | \
  while read -d $'\0' f; do mv -v "$f" "${f// /_}"; done

findprocurará arquivos com um espaço no nome. Os nomes de arquivos serão impressos com um nullbyte ( -print0) como delimitador para também lidar com nomes de arquivos especiais. Em seguida, o readbuilt-in lê os nomes de arquivos delimitados pelo nullbyte e, finalmente,mv substitui os espaços por um sublinhado.

EDIT: Se você deseja remover os espaços nos diretórios também, é um pouco mais complicado. Os diretórios são renomeados e, em seguida, não são mais acessíveis pelo nome findencontrado. Tente o seguinte:

find -name "* *" -print0 | sort -rz | \
  while read -d $'\0' f; do mv -v "$f" "$(dirname "$f")/$(basename "${f// /_}")"; done

A sort -rzinverte a ordem de arquivo, para que os arquivos mais profundos em uma pasta é o primeiro a se mover e a própria pasta será a última. Portanto, nunca há pastas renomeadas antes que todos os arquivos e pastas sejam renomeados dentro dela. O mvcomando no loop também é um pouco alterado. No nome do destino, removemos apenas os espaços no nome de base do arquivo, caso contrário, ele não estaria acessível.

caos
fonte
Estou tentando substituir espaços com sublinhado em todo o diretório, mas está dando erro porque, depois de alterar o nome, esse diretório não está acessível.
precisa
@krishna Adicionei uma edição à minha resposta.
caos
1
@chaos wow, apenas correu estes dois em dois sistemas e funcionou como um encanto, alguns arquivos e diretórios não passando, mas é apenas alguns
somethingSomething
No macOS, precisamos especificar o diretório no comando find antes da opção -name find . -name "* *" -print0 | sort -rz | \ while read -d $'\0' f; do mv -v "$f" "$(dirname "$f")/$(basename "${f// /_}")"; done
Laszlo Pinter
16
  1. Usando rename

    find . -type f -name "* *.xml" -exec rename "s/\s/_/g" {} \;

    ou com $1

    find "$1" -type f -name "* *.xml" -exec rename "s/\s/_/g" {} \;
  2. Usando mv

    find . -type f -name "* *.xml" -exec bash -c 'mv "$0" "${0// /_}"' {} \;

    ou com $1

    find "$1" -type f -name "* *.xml" -exec bash -c 'mv "$0" "${0// /_}"' {} \;
AB
fonte
Não sei por que, mas seu comando não está funcionando para mim. Não está mostrando nenhum erro e saída.
krishna
@krishna corrigido, sorry
AB
1
Renomear funcionou para mim. Soquinho!
Brian Piercy
1

Este é um método que encontrei ao enfrentar o mesmo problema:

for f in *; do mv "$f" `echo $f | tr ' ' '_'`; done

Eu estava escrevendo um arquivo de script bash para atualizar automaticamente meus certificados ssl.

NekoMisaki
fonte
1

Use rename:

rename 's/\s/_/g' ./*.xml

Não há necessidade de find:)

Jan Werkhoven
fonte
1
adicione o g final ao regex e ele funcionará perfeitamente. 's / \ s / _ / g'
jbrahy 5/11/19
Boa dica, obrigado :)
Jan Werkhoven