W systemie plików, w którym nazwy plików znajdują się w UTF-8, mam plik o błędnej nazwie; jest wyświetlany jako :, D�sinstaller
rzeczywista nazwa według zsh D$'\351'sinstaller
:, Latin1 dla Désinstaller
, sam francuski barbarzyństwo dla „odinstaluj”. Zsh nie [[ $file =~ '^.*$' ]]
pasowałby do tego, ale pasowałby do niego globbingiem *
- takiego zachowania się oczekuję.
Teraz nadal oczekuję, że znajdę go podczas działania find . -name '*'
- w rzeczywistości nigdy nie spodziewałbym się, że nazwa pliku nie przejdzie tego testu. Jednak z LANG=en_US.utf8
, plik nie pojawia się i muszę ustawić LANG=C
(lub en_US
, lub ''
), aby działał.
Pytanie: Jakie jest wdrożenie i jak mogłem przewidzieć ten wynik?
Informacje: Arch Linux 3.14.37-1-lts, find (GNU findutils) 4.4.2
convmv
konwersję nazw plików na utf-8?[[ $file =~ '^.*$' ]]
tym, że nie używamrecode
nazwy pliku, ale teraz zajrzę wconvmv
razie potrzeby. Dzięki.Odpowiedzi:
To naprawdę niezły haczyk. Po krótkim spojrzeniu na kod źródłowy GNU find, powiedziałbym, że sprowadza się to do tego, jak
fnmatch
zachowuje się w nieprawidłowych sekwencjach bajtów (pred_name_common
inpred.c
):Ten kod testuje zwracaną wartość
fnmatch
równości z 0, ale nie sprawdza błędów; powoduje to zgłaszanie wszelkich błędów jako „nie pasuje”.Wiele lat temu zasugerowano zmianę zachowania tej funkcji libc, aby zawsze zwracała wartość true we
*
wzorcu, nawet w przypadku zepsutych nazw plików, ale z tego, co mogę powiedzieć, pomysł musiał zostać odrzucony (patrz wątek rozpoczynający się na https : //sourceware.org/ml/libc-hacker/2002-11/msg00071.html ):Jak wspomniał Stéphane Chazelas w komentarzu, a także w tym samym wątku z 2002 r., Jest to niezgodne z rozszerzeniem globalnym wykonywanym przez powłoki, które nie dławią się nieprawidłowymi postaciami. Być może jeszcze bardziej zagadkowe jest to, że cofnięcie testu będzie pasowało tylko do plików, które mają niepoprawne nazwy (twórz pliki w skrócie
touch $'D\351marrer' $'Touch\303\251' $'\346\227\245\346\234\254\350\252\236'
):Tak więc, aby odpowiedzieć na twoje pytanie, mógłbyś to przewidzieć, znając swoje zachowanie
fnmatch
w tym przypadku i wiedząc, jakfind
obsługuje wartość zwracaną przez tę funkcję; prawdopodobnie nie mogłeś się tego dowiedzieć, czytając dokumentację.źródło
*
, że byłoby to niespójneD*staller
.D*staller
będzie pasować$'D\351sinstaller'
jak dobrze jak to ma miejsce w glob wszystkich pocisków, które testowałem. Biorąc pod uwagę, że zachowanie GNU fnmatch nie jest zgodne z zachowaniem powłoki GNU, powiedziałbym, że to błąd..
powinny one pasować tylko do prawidłowych znaków w kodowaniu - stąd moje oczekiwania, które.*
nie pasują do niepoprawnych łańcuchów - ale nie mogę znaleźć pasującej specyfikacji dla globującej gwiazdy.-name '*'
wszystkich plików, w tym zepsutymi nazwami), więc prawdopodobnie wersja BSDfnmatch
, która nie deklaruje zgodności z POSIX.2, w przeciwieństwie do wersji GNU, ma inną i prawdopodobnie rozsądniejszą interpretację tego, co należy zrobić w przypadku nieprawidłowych znaków.-name
Opcja find używa notacji dopasowującej wzór powłoki do wykonania pasującej nazwy pliku.*
to wzór pasujący do wielu znaków , powinien pasować ciąg zerowy lub więcej znaków.find
używa fnmatch do sprawdzania dopasowania wzorca, więc możesz użyć ltrace do sprawdzenia wyniku:Z
D\351sinstaller
,fnmatch
return-1
, wskazuje, że nie udało się dopasować. Prawidłowy znak jakሒaa
zostanie dopasowany.W twoim przypadku, z
UTF-8
ustawieniami regionalnymi,\351
jest niepoprawnym znakiem, co powoduje niepowodzenie dopasowania wzorca.źródło
ltrace
. Wiedziałem o tymstrace
, aleltrace
jest dla mnie nowy. Śliczny!