zbiorcza zmiana nazw (lub prawidłowe wyświetlanie) plików ze znakami specjalnymi

20

Mam kilka katalogów i podkatalogów, które zawierają pliki ze znakami specjalnymi, takie jak ten plik:

robbie@phil:~$ ls testsktest.txt 
test?sktest.txt

Znajdź ujawnia sekwencję ucieczki:

robbie@phil:~$ find testsktest.txt -ls 
424512 4000 -rwxr--r-x   1 robbie   robbie    4091743 Jan 26 00:34 test\323sktest.txt

Jedynym powodem, dla którego mogę nawet wpisać ich nazwy w konsoli, jest uzupełnianie tabulatorów. Oznacza to również, że mogę zmienić ich nazwę ręcznie (i usunąć znak specjalny).

Ustawiłem LC_ALL na UTF-8, co wydaje się nie pomagać (również nie w nowej powłoce):

robbie@phil:~$ echo $LC_ALL
en_US.UTF-8

Łączę się z maszyną za pomocą ssh z mojego komputera Mac. Jest to instalacja Ubuntu:

robbie@phil:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=7.10
DISTRIB_CODENAME=gutsy
DISTRIB_DESCRIPTION="Ubuntu 7.10"

Shell to Bash, TERM jest ustawiony na kolor xterm.

Pliki te istnieją już od dłuższego czasu i nie zostały utworzone przy użyciu tej instalacji Ubuntu. Nie wiem więc, jakie były kiedyś ustawienia kodowania systemu.

Próbowałem rzeczy w następujący sposób:

find . -type f -ls | sed 's/[^a-zA-Z0-9]//g'

Ale nie mogę znaleźć rozwiązania, które robi wszystko, co chcę:

  1. Zidentyfikuj wszystkie pliki, których nie można wyświetlić (powyższe ignoruje zdecydowanie za dużo)
  2. Dla wszystkich tych plików w drzewie katalogów (rekurencyjnie) wykonaj mv stara nazwa nowa nazwa
  3. Opcjonalnie możliwość transliteracji znaków specjalnych, takich jak ä na a (nie jest wymagane, ale byłoby niesamowite)

LUB

  1. Prawidłowo wyświetl wszystkie te pliki (i nie ma błędów w aplikacjach podczas próby ich otwarcia)

Mam drobiazgi, takie jak iteracja po wszystkich plikach i przenoszenie ich, ale identyfikowanie plików i prawidłowe formatowanie ich dla polecenia mv wydaje się być trudną częścią.

Wszelkie dodatkowe informacje na temat tego, dlaczego nie wyświetlają się poprawnie lub jak „odgadnąć” prawidłowe kodowanie są również mile widziane. (Próbowałem convmv, ale wydaje się, że nie robi dokładnie tego, czego chcę: http://j3e.de/linux/convmv/ )

RobbieV
źródło
Poniższa pojedyncza odpowiedź jest zgodna z pierwszym sposobem (znajdź je i zmień nazwę na nowe kodowanie), ale drugi sposób byłby również interesujący: teraz, gdy znasz kodowanie używane dla zdalnych nazw plików, jak ssh do zdalnego hosta w takim sposób, w jaki nazwy plików są wyświetlane poprawnie (i można nimi zarządzać, wpisując ich nazwy za pomocą klawiatury)?
imz - Ivan Zakharyaschev

Odpowiedzi:

21

Wydaje mi się, że widzisz ten nieprawidłowy znak, ponieważ nazwa zawiera sekwencję bajtów, która nie jest poprawna UTF-8. Nazwy plików w typowych systemach plików uniksowych (w tym także w twoim) są łańcuchami bajtów i od aplikacji zależy, jakiego kodowania użyć. Obecnie istnieje tendencja do używania UTF-8, ale nie jest to uniwersalne, szczególnie w lokalizacjach, które nigdy nie mogłyby żyć z czystym ASCII i korzystały z innych kodowań, zanim jeszcze istniała UTF-8.

Spróbuj LC_CTYPE=en_US.iso88591 lssprawdzić, czy nazwa pliku ma sens w ISO-8859-1 (latin-1). Jeśli nie, wypróbuj inne ustawienia regionalne. Pamiętaj, że tylko LC_CTYPEustawienie regionalne ma tutaj znaczenie.

W ustawieniach regionalnych UTF-8 następujące polecenie pokaże wszystkie pliki, których nazwa jest niepoprawna UTF-8:

grep-invalid-utf8 () {
  perl -l -ne '/^([\000-\177]|[\300-\337][\200-\277]|[\340-\357][\200-\277]{2}|[\360-\367][\200-\277]{3}|[\370-\373][\200-\277]{4}|[\374-\375][\200-\277]{5})*$/ or print'
}
find | grep-invalid-utf8

Możesz sprawdzić, czy mają one większy sens w innym języku za pomocą polecenia recode lub iconv :

find | grep-invalid-utf8 | recode latin1..utf8
find | grep-invalid-utf8 | iconv -f latin1 -t utf8

Po ustaleniu, że kilka nazw plików jest w pewnym kodowaniu (np. Latin1), jednym ze sposobów ich zmiany jest

find | grep-invalid-utf8 |
rename 'BEGIN {binmode STDIN, ":encoding(latin1)"; use Encode;}
        $_=encode("utf8", $_)'

Używa komendy perl rename dostępnej w Debianie i Ubuntu. Możesz przekazać go, -naby pokazać, co by to zrobił bez faktycznej zmiany nazw plików.

Gilles „SO- przestań być zły”
źródło
Dzięki, wypróbuję dzisiaj niektóre z tych rzeczy! Wygląda na to, że to będzie zaakceptowana odpowiedź :)
RobbieV
Znaleźć | Wydaje się, że polecenie grep „[[: print:]]” po prostu zwraca wszystkie pliki. Czy UTF-8 nie powinien być kompatybilny z wieloma innymi kodowaniami ze znakami „normalnymi”?
RobbieV
@RobbieV: Pisałem na maszynie i chciałem grep [^[:print:]]szukać znaków, które nie da się wydrukować. Ale właśnie przetestowałem z GNU grep i nieprawidłowe sekwencje UTF-8 nie zostały złapane [^[:print:]](co ma sens, ponieważ nie są to znaki niedrukowalne, w ogóle nie są znakami). Edytowałem swój post, używając dłuższego sposobu grepowania linii z nieprawidłowymi sekwencjami utf8. Zauważ, że poprawiłem także kierunek recodei iconvprzykładów.
Gilles „SO - przestań być zły”,
To działało idealnie. Wypróbowałem wszystkie polecenia oprócz iconv one i wszystkie działają zgodnie z oczekiwaniami. Czysta magia!
RobbieV
Nawet sugerowane kodowanie Latin1 było prawidłowe :)
RobbieV
1

Wiem, że to stare pytanie, ale całą noc szukałem podobnego rozwiązania. Znalazłem kilka pomocnych wskazówek, ale nie zrobiły dokładnie tego, czego potrzebowałem, więc musiałem wymieszać i dopasować kilka, aby uzyskać poprawny wynik, którego szukałem

aby po prostu usunąć znaki specjalne i zastąpić je kropką (.)

for f in *.txt; do mv "$f" `echo $f | sed "s/[^a-zA-Z0-9.]/./g"`; done

aby użyć w cronjob robiłem co następuje, aby uruchomić co minutę

*/1 * * * * cd /path/to/files/ && for f in *.txt; do mv "$f" `echo $f | sed "s/[^a-zA-Z0-9.]/./g"`; done >/dev/null 2>&1

Mam nadzieję, że ktoś uzna to za pomocne, ponieważ sprawiło, że mój dzień :)

Topps70
źródło
(1) Dla jasności możesz zmienić `…`na $(…)- zobacz to , to i to . (2) Zawsze powinieneś cytować odniesienia do zmiennych powłoki (np. "$f"), Chyba że masz dobry powód, aby tego nie robić i jesteś pewien, że wiesz, co robisz. Dotyczy to nawet echo "$f" | sed …. Dotyczy to również całego $(…)(lub `…`) wyrażenia; tj mv "$f" "$(echo "$f" | sed "…")". … (Ciąg dalszy)
Scott
(Ciąg dalszy)… (3) Powinieneś powiedzieć , aby chronić przed nazwami plików zaczynającymi się od . (4) Jeśli masz pliki o nazwach „foo ♥ bar.txt” i „foo ♠ bar.txt”, to (spróbuje) zmienić ich nazwy na „foo.bar.txt”, prawdopodobnie powodując wszystkie oprócz jednego pliki do zniszczenia. (5) Dlaczego, u licha, chcesz to robić raz na minutę? mv -- "$f" …-
Scott
Mam skrypt torrentowy, który automatycznie pobiera pliki. a czasem niektóre pliki zawierają znaki, które wyrzucają przesyłającego. więc po prostu zmieniając nazwy plików ze znakami specjalnymi, mój cron rozwiązał wszystkie moje problemy, a program przesyłający wykonuje swoje zadanie płynnie.
Topps70
więc (ten plik, który był - pobrany_ tekst) zmienia się w (this.fi.le.tha.t.was.down.loaded.ext)
Topps70
0

Teraz, gdy wiesz, jakie kodowanie jest używane dla nazw plików na odległym końcu („latin1” - zgodnie z komentarzami do pierwszej odpowiedzi), możesz również postępować w drugą stronę - uruchomić lokalny terminal i ssh w takim sposób, w jaki zdalne nazwy plików są wyświetlane poprawnie (a nie pierwszy sposób: zmień ich nazwy) .

Tak jak ja , możesz uruchomić terminal lokalnie, który działałby w tym specjalnym kodowaniu, być może tak:

LC_ALL = en_US.latin1 xvt &

xvt oznacza twój program terminalowy.

Być może istniejące ustawienia narodowe są nazywane en_US.iso88591, a nie en_US.latin1, jak zakładałem.

imz - Ivan Zakharyaschev
źródło
0

To nie spełnia wymagań masowych, ale właśnie miałem podobny problem, w którym miałem wiele wersji pliku o podobnych nazwach, które różniły się tylko pojedynczym dziwnym znakiem. Niestety oznaczało to, że nie mogłem zmienić nazwy przestępców za pomocą sztuczki z symbolami wieloznacznymi, której zwykle używam.

W końcu użyłem Filezilli do połączenia się jako klient SFTP, przejrzałem pliki i zmieniłem ich nazwy za pomocą GUI. Filezilla całkiem dobrze radziła sobie z podejrzanymi postaciami.

kabadisha
źródło