Jak zastąpić biały znak podkreśleniem we wszystkich nazwach plików?

2

Podany poniżej skrypt umieszcza „podkreślenie” zamiast „białych znaków” we wszystkich nazwach plików w określonym folderze. Mam problem z tworzeniem skryptu powłoki, który umieszcza „podkreślenie” zamiast „białych znaków” w nazwach wszystkich podfolderów i zawartych w nich plików, a nie tylko w folderze.

Czy ktoś ma jakieś wskazówki, jak to zrobić?

Oto mój kod:

#!/bin/bash
ls | while read -r FILE; do
  mv -v "$FILE" `echo $FILE | tr ' ' '_'`
done
Rafael
źródło
Przeciągnij wyjście finddo swojej whilepętli zamiast ls. Najbezpieczniej byłoby przeczytać instrukcję i użyć opcji takich jak -maxdepth. Możesz także spojrzeć na -execopcję całkowitego uniknięcia pętli.
David

Odpowiedzi:

3

Użyj find do wyszukiwania w katalogach i podkatalogach:

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 - Drukuje wszystkie znalezione ścieżki do plików w bieżącym katalogu i podkatalogach.

Dzięki procesowi podstawienia wyjście polecenia find jest wysyłane do pętli while, gdzie odczytuje zmienną fname.

nname="${fname##*/}" - Wyodrębnia nazwę pliku ze ścieżki

"${fname%/*}" - wyodrębnia ścieżkę

"${nname//[[:space:]]/"_"}" - zamienia spacje w nazwie pliku na _

"${fname%/*}/${nname//[[:space:]]/"_"}" - ścieżka / nowa_nazwa_pliku

Wilf
źródło
Musisz usunąć podwójne cudzysłowy wokół znaku podkreślenia; ponieważ jest już w cudzysłowie, najwyraźniej są traktowane jako dosłowne znaki i kończą się nazwą pliku. Również polecam dodanie podwójne cudzysłowy wokół $(pwd)w przypadku, gdy zawiera spacje (lub po prostu użyć .zamiast) i dodać -name "* *"do findpolecenia, aby pominąć pliki, które nie mają miejsca i dodać albo -iczy -ndo mvkomendy, aby uniknąć utraty danych, jeśli istnieje konflikt nazwy pliku.
Gordon Davisson
Słuszne uwagi. Podwójne cudzysłowy wewnątrz podwójnych cudzysłowów działają w mojej powłoce, ale ja też to zmienię. Dzięki.
1
Ciekawy. Najwyraźniej sposób traktowania podwójnych cudzysłowów w tym kontekście zmienił się kiedyś między wersją bash v4.2 a bash v4.4. Testowałem z bash v3; prawdopodobnie masz nowszą wersję.
Gordon Davisson
3

Zamiast tego użyj renamenarzędzia , np

rename "s/ /_/g" *

Skrypt do zmiany nazw plików obsługiwany przez Perla z wieloma pomocnymi wbudowanymi funkcjami.


Aby zmienić nazwę rekurencyjną, spróbuj:

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

gdzie **jest opcja globowania Bash (włącz przez shopt -s globstar).

Alternatywnie użyj findnp

find . -type f -execdir rename "s/ /_/g" {} ';'
kenorb
źródło
0

W bash(nie jestem pewien co do pochodnych i sh!) Możesz używać podstawiania zmiennych itp. W zmiennych

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

JEDNAK TO co najmniej nie zajmuje się innymi znakami spacji (tab itp.)

Wilf
źródło
Dziękuję Wilf! Nadal nie mogę. Wkrótce bardziej uważnie zobaczę twoją sugestię. Skrypt, który umieściłem powyżej, wstawia „podkreślenie” pomiędzy wszystkimi słowami, które mają „białe znaki”. Chciałbym zrobić to samo w przypadku plików znajdujących się w podfolderach
Rafael
0

Moje podejście:

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

Wersja wieloliniowa dla czytelności:

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

Wyjaśnienie:

  • find . -name "* *"znajduje obiekty, których nazwy należy zmienić. Notatka findjest bardzo elastyczna dzięki swoim testom, więc jeśli chcesz (np.) Zmieniać nazwy tylko katalogów, zacznij od find . -depth -type d -name "* *".
  • -execdirwykonuje podany proces ( bash) w katalogu, w którym znajduje się obiekt, więc każda przekazywana ścieżka {}jest zawsze taka ./bar, jak nie ./foo/bar. Oznacza to, że nie musimy przejmować się całą ścieżką. Minusem jest to, mv -vże nie pokazuje ci ścieżki, więc dodałem pwdtylko dla informacji (możesz ją pominąć, jeśli chcesz).
  • bashpozwala nam użyć "${baz// /_}"składni.
  • -depthzapewnia, że ​​nic się nie stanie: findzmieni nazwę katalogu (jeśli dotyczy), a następnie spróbuje przetworzyć jego zawartość starą ścieżką.
  • {} +jest w stanie wyżywić bashsię z wielu obiektów (w przeciwieństwie do {} \;składni). Iterujemy nad nimi for f in "$@". Nie chodzi o to, aby uruchamiać osobny bashproces dla każdego obiektu, ponieważ utworzenie nowego procesu jest kosztowne. Myślę, że nie możemy łatwo uniknąć uruchamiania osobnych opcji mv; Mimo to zmniejszenie liczby bashwywołań wydaje się dobrą optymalizacją ( pwdjest wbudowane bashi nie kosztuje nas procesu). Jednak -execdir ... {} +nie będą przekazywać plików z różnych katalogów jednocześnie. Używając -exec ... {} +zamiast, -execdir ... {} +możemy dodatkowo zmniejszyć liczbę procesów, ale wtedy musimy dbać o ścieżki, a nie tylko nazwy plików (porównaj tę inną odpowiedź , wydaje się, że wykonuje przyzwoitą robotę, ale while readspowalnia ją). Jest to kwestia prędkości w porównaniu z (względną) prostotą. Moje rozwiązanie z -execjest poniżej.
  • dummytuż przed tym {}staje się $0w naszym bash. Potrzebujemy tego fałszywego argumentu, ponieważ "$@"jest on równoważny "$1" "$2" ...(nie "$0" "$1" ...). W ten sposób wszystko, co przeszło, {}jest dostępne później jako "$@".

Bardziej złożona, nieco zoptymalizowana wersja (różne ${...}sztuczki zaczerpnięte z innej odpowiedzi ):

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

Inne podejście (eksperymentalne!) Obejmuje vidir. Sztuką są vidirzastosowania, $EDITORktóre mogą nie być interaktywnym edytorem :

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

Ostrzeżenia:

  • Nie powiedzie się to w przypadku nazw plików / katalogów ze znakami specjalnymi (np. Znak nowej linii).
  • Nie możemy użyć s/ /_/gbezpośrednio, \d32jest to obejście.
  • Ze względu na to, jak vidirdziała, podejście byłoby trudne, jeśli chcesz zastąpić cyfrę lub tabulator.
  • Tutaj vidirdziała ze ścieżkami, nie tylko nazwami plików (nazwami podstawowymi), dlatego zmiana nazw tylko plików (tj. Nie katalogów) może być trudna.

Niemniej jednak, jeśli wiesz, co robisz, może to wykonać pracę jeszcze szybciej. Nie polecam jednak takiego (ab) użycia vidirw ogólnym przypadku. Włączyłem to do mojej odpowiedzi, ponieważ uważałem to eksperymentalne podejście za interesujące.

Kamil Maciorowski
źródło