Jak zamienić spacje we wszystkich nazwach plików na podkreślenia w systemie Linux za pomocą skryptu powłoki?

17

Próbowałem wykonać następujący skrypt powłoki, który powinien zastąpić spacje ze wszystkich nazw plików 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

Załóżmy, że mam plik xml o nazwie xy z.xml, a następnie daje:

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
źródło

Odpowiedzi:

29

Użyj tego z bash:

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

findwyszuka pliki ze spacją w nazwie. Nazwy plików zostaną wydrukowane z nullbyte ( -print0) jako separatorem, aby poradzić sobie również ze specjalnymi nazwami plików. Następnie readwbudowany odczytuje nazwy plików rozdzielone przez nullbyte i ostatecznie mvzastępuje spacje znakiem podkreślenia.

EDYCJA: Jeśli chcesz usunąć spacje również w katalogach, jest to trochę bardziej skomplikowane. Nazwy katalogów są zmieniane, a następnie niedostępne dla findznalezisk nazw . Spróbuj tego:

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

sort -rzOdwraca kolejność plików, tak że najgłębsze pliki w folderze są pierwszymi, aby przenieść i sam folder będzie ostatni. Dlatego nigdy nie zmieniano nazw folderów, zanim wszystkie pliki i foldery nie zostaną w nim zmienione. mvPolecenia w pętli jest nieco zmienił też. W nazwie docelowej usuwamy tylko spacje w nazwie basenu pliku, w przeciwnym razie nie byłby dostępny.

chaos
źródło
Próbuję zastąpić spacje znakiem podkreślenia we wszystkich katalogach, ale powoduje to błąd, ponieważ po zmianie nazwy ten katalog nie jest dostępny.
krishna
@ krishna Dodałem zmianę do mojej odpowiedzi.
chaos
1
@chaos wow, po prostu zabrakło tych dwóch na dwóch systemach i to działało jak czar, kilka plików i katalogów nie przechodzi, ale to tylko kilka
somethingSomething
W systemie macOS musimy podać katalog w poleceniu find przed opcją -name find . -name "* *" -print0 | sort -rz | \ while read -d $'\0' f; do mv -v "$f" "$(dirname "$f")/$(basename "${f// /_}")"; done
Laszlo Pinter,
17
  1. Za pomocą rename

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

    lub z $1

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

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

    lub z $1

    find "$1" -type f -name "* *.xml" -exec bash -c 'mv "$0" "${0// /_}"' {} \;
AB
źródło
Nie wiem dlaczego, ale twoje polecenie nie działa dla mnie. Nie wyświetla żadnych błędów i wyników.
krishna
@krishna poprawiony, przepraszam
AB
1
Rename pracowała dla mnie. Fistbump!
Brian Piercy,
1

Jest to metoda, którą znalazłem, mając do czynienia z tym samym problemem:

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

Pisałem plik skryptu bash, aby automatycznie aktualizować moje certyfikaty ssl.

NekoMisaki
źródło
1

Użyj rename:

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

Nie ma potrzeby find:)

Jan Werkhoven
źródło
1
dodaj końcowy g do wyrażenia regularnego i działa idealnie. 's / \ s / _ / g'
jbrahy
Dobra rada, dziękuję :)
Jan Werkhoven