Jak masowo zmieniać nazwy plików z niepoprawnym kodowaniem lub masowo zamieniać niepoprawne kodowane znaki?

15

Mam serwer debian i prowadzę muzykę do internetowej stacji radiowej. Mam problem z nazwami plików i ścieżkami, ponieważ wiele plików ma nieprawidłowe kodowanie, na przykład:

./music/Bändname - Some Title - additional Info/B�ndname - 07 - This Title Is Cörtain, The EncÃding Not.mp3

Idealnie chciałbym usunąć wszystko, co nie jest literami A-Z/ a-zcyframi 0-9lub myślnikiem -/ podkreśleniem _... Wynik powinien wyglądać mniej więcej tak:

./music/Bndname-SomeTitle-additionalInfo/Bndname-07-ThisTitleIsCrtain,TheEncdingNot.mp3

Jak to zrobić dla partii wielu plików i katalogów?

Widziałem podobne pytanie: zmiana nazwy zbiorczej (lub prawidłowe wyświetlanie) plików ze znakami specjalnymi

Ale to tylko naprawia kodowanie, wolałbym bardziej rygorystyczne podejście, jak opisano powyżej.

Afr
źródło

Odpowiedzi:

14

Będziesz miał problemy, jeśli chcesz zmienić nazwy plików i katalogów w tym samym czasie. Zmiana nazwy tylko pliku jest dość łatwa. Ale chcesz mieć pewność, że nazwy katalogów również zostaną zmienione. Nie można po prostu mv Motörhead/Encöding Motorhead/Encodingponieważ Motorheadnie będzie istnieć w momencie wywołania.

Potrzebujemy więc dogłębnego przejścia wszystkich plików i folderów, a następnie zmień nazwę tylko bieżącego pliku lub folderu. Poniższe działa z GNU findi Bash 4.2.42 na moim OS X.

#!/usr/bin/env bash
find "$1" -depth -print0 | while IFS= read -r -d '' file; do
  d="$( dirname "$file" )"
  f="$( basename "$file" )"
  new="${f//[^a-zA-Z0-9\/\._\-]/}"
  if [ "$f" != "$new" ]      # if equal, name is already clean, so leave alone
  then
    if [ -e "$d/$new" ]
    then
      echo "Notice: \"$new\" and \"$f\" both exist in "$d":"
      ls -ld "$d/$new" "$d/$f"
    else
      echo mv "$file" "$d/$new"      # remove "echo" to actually rename things
    fi
  fi
done

Możesz zmienić wyrażenie regularne, używając, new="${f//[\\\/\:\*\?\"<>|]/}"jeśli chcesz zastąpić wszystko, czego system Windows nie może obsłużyć.

Zapisz ten skrypt jako rename.sh, aby można go było wykonać za pomocą chmod +x rename.sh. Następnie nazwij to tak rename.sh /some/path.

Pamiętaj, aby rozwiązać wszelkie kolizje nazw plików (komunikaty „ Notice”).

Jeśli masz absolutną pewność, że dokonuje prawidłowych zamian, usuń echoskrypt ze skryptu, aby zmienić nazwy rzeczy, zamiast drukować tylko to, co robi.

Dla bezpieczeństwa zalecam najpierw przetestowanie tego na małym podzbiorze plików.


Opcje wyjaśnione

Aby wyjaśnić, co się tutaj dzieje:

  • -depthupewni się, że katalogi są rekurencyjnie najpierw na głębokości, abyśmy mogli „zwinąć” wszystko od końca. Zwykle findtrawersuje inaczej (ale nie szerokość).
  • -print0zapewnia, że finddane wyjściowe są rozdzielane zerami, dzięki czemu możemy odczytać je read -d ''w filezmiennej. Takie postępowanie pomaga nam radzić sobie z wszelkiego rodzaju dziwnymi nazwami plików, w tym ze spacjami, a nawet znakami nowej linii.
  • Otrzymamy katalog pliku z dirname. Nie zapomnij, aby zawsze poprawnie cytować zmienne, w przeciwnym razie ścieżka ze spacjami lub znakami globowania złamałaby ten skrypt.
  • Otrzymamy rzeczywistą nazwę pliku (lub nazwę katalogu) za pomocą basename.
  • Następnie usuwamy wszelkie nieprawidłowe znaki z $fkorzystania z możliwości zamiany ciągów Basha. Nieprawidłowy oznacza wszystko, co nie jest małą lub wielką literą, cyfrą, ukośnikiem ( \/), kropką ( \.), podkreśleniem lub łącznikiem minus.
  • Jeśli $fjest już czysty (wyczyszczona nazwa jest identyczna z bieżącą nazwą), pomiń go.
  • Jeśli $newjuż istnieje w katalogu $d(np. Masz pliki o nazwach resumei résuméw tym samym katalogu), wydaj ostrzeżenie. Nie chcesz zmieniać jego nazwy, ponieważ w niektórych systemach mv foo foopowoduje problem. Inaczej,
  • W końcu zmieniamy nazwę oryginalnego pliku (lub katalogu) na jego nową nazwę

Ponieważ będzie to działać tylko w najgłębszej hierarchii, zmiana nazwy Motörhead/Encödingna Motorhead/Encodingodbywa się w dwóch krokach:

  1. mv Motörhead/Encöding Motörhead/Encoding
  2. mv Motörhead Motorhead

Zapewnia to, że wszystkie wymiany są wykonywane we właściwej kolejności.


Przykładowe pliki i uruchomienie testowe

Załóżmy, że niektóre pliki w folderze podstawowym o nazwie test:

test
test/Motörhead
test/Motörhead/anöther_file.mp3
test/Motörhead/Encöding
test/Randöm
test/Täst
test/Täst/Töst
test/with space
test/with-hyphen.txt
test/work
test/work/resume
test/work/résumé
test/work/schedule

Oto dane wyjściowe z uruchomienia w trybie debugowania (z echoprzodu przed mv), tj. Poleceń, które zostaną wywołane, i ostrzeżeń o kolizji:

mv test/Motörhead/anöther_file.mp3 test/Motörhead/another_file.mp3
mv test/Motörhead/Encöding test/Motörhead/Encoding
mv test/Motörhead test/Motorhead
mv test/Randöm test/Random
mv test/Täst/Töst test/Täst/Tost
mv test/Täst test/Tast
mv test/with space test/withspace
Notice: "resume" and "résumé" both exist in test/work:
-rw-r—r--  …  …  test/work/resume
-rw-r—r--  …  …  test/work/résumé

Zwróć uwagę na brak komunikatów o with-hyphen.txt, schedulei testsama.

slhck
źródło
1
Możesz dodać logikę, aby obsłużyć przypadek, w którym miejsce docelowe mvjuż istnieje, co może się zdarzyć (1), jeśli masz pliki, które są już czyste (w wyniku mv foo foo) lub (2), jeśli masz pliki o tej samej nazwie, z wyjątkiem dla znaków specjalnych (np. mv Encöding Encodinggdzie już masz Encodingplik oprócz Encöding).
Scott,
Dobry pomysł, dzięki. Jakieś konkretne sugestie, co robić w takim przypadku? To prawda - osiągnięcie tego w czysty i zdrowy sposób jest trudniejsze, niż się wydaje. Jeśli masz coś, możesz go oczywiście edytować.
slhck
Nie sądzę, aby sensowne było automatyczne myślenie o kolizjach - wystarczy zidentyfikować je dla użytkownika i pozwolić mu sobie z nimi poradzić. Zredagowałem twoją odpowiedź, jak zasugerowałeś.
Scott
+1 za użycie przykładu z „Encöding” Za dużo fön! :-)
Marcel
Po trzech latach wciąż tu wracam. bardzo przydatne! :-)
Afr
15

Wiem, że nie jest to dokładnie to, czego chciałeś, ale jeśli znasz oryginalne kodowanie, być może możesz convmvzmienić kodowanie na UTF-8, co powinno rozwiązać większość problemów.

To działało dla mnie w folderze z kilkoma niepoprawnie zakodowanymi polskimi nazwami plików:

convmv -f cp1250 -t utf8 -r .

Zauważ, że to polecenie w rzeczywistości niczego nie zmienia nazwy; dodaj --notestopcję, aby naprawdę zmienić nazwy plików.

mik01aj
źródło
1
Dla tych, którzy mają statyczny zestaw (lub nie mają zróżnicowanej kombinacji zestawów znaków), convmvopcja jest niezwykle prosta i idealna. W przypadku OP, który ma potencjalnie wiele zestawów znaków, można by to połączyć z inną odpowiedzią, ponieważ convmvwydaje się, że wiadomo, kiedy to się pojawia, a kiedy nie. Pętlując przez zestawy znaków, przez convmv --list, można je poprawnie zakodować.
1
Rozumiem przez to, że jeśli jako OP działa serwer Debian, na pewno dziś zakładamy UTF8, w którym to przypadku można zachować oryginalne litery. Miałem folder z niektórymi znakami nordyckimi i użyłem: convmv -t utf8 --nfc -f iso-8859-1 --notest -r .- Miałem --nfcsię dostosować do Linuksa przed OS X, więc po prostu pisanie convmvrezygnuje z (użytecznych) opcji.
0

Wiem, że pytałeś o zmianę nazwy.

Możesz jednak dość łatwo uniknąć problemu za pomocą oprogramowania takiego jak MusicBrainz Picard .

Jest w stanie identyfikować muzykę (odciski palców audio), pobierać wszystkie niezbędne dane (w tym obrazy okładki, jeśli są dostępne) z ogromnej bazy danych MusicBrainz i przenosić pliki, aby twoja kolekcja pasowała do dowolnego wzoru. Używam go od lat i zawsze działało idealnie z czymkolwiek, od cyrylicy po arabski; i oczywiście (przynajmniej w przypadku skryptów opartych na języku łacińskim) może również dokonać konwersji do ASCII.

Przy takim podejściu tak naprawdę nie ma znaczenia, jak niechlujna / źle nazwana jest twoja kolekcja, pod warunkiem, że pliki są czytelne i kompletne.

(Czy wspominałem, że to nic nie kosztuje? Zarówno w mowie, jak i w darmowym piwie? Zarówno oprogramowanie, jak i baza danych ...?)

Alois Mahdal
źródło