Jest to konsekwencja tego, że postacie mają tę samą kolejność sortowania.
Zauważysz to również
sort -u << EOF
■
⅕
⅖
⅗
EOF
zwraca tylko jedną linię.
Albo to:
expr ■ = ⅕
zwraca true (zgodnie z wymaganiami POSIX).
Większość ustawień narodowych dostarczanych z systemami GNU ma wiele znaków (a nawet sekwencje znaków (sekwencje zestawiania)), które mają tę samą kolejność sortowania. W przypadku tych ■ ⅕⅖⅗ dzieje się tak dlatego, że kolejność nie jest zdefiniowana, a znaki, których kolejność nie jest zdefiniowana, mają w systemach GNU tę samą kolejność sortowania. Są znaki, które są wyraźnie zdefiniowane jako mające tę samą kolejność sortowania, jak Ș i Ş (chociaż nie ma (jak dla mnie zresztą) prawdziwej logiki ani spójności w tym, jak to się robi).
To jest źródło dość zaskakujących i fałszywych zachowań. I podniósł kwestię bardzo niedawno na grupie Austin (ciało za POSIX i Single Unix Specification) listy i dyskusja wciąż trwa od dnia 2015-04-03.
W tym przypadku pytanie, czy [y]
powinien pasować do tego samego x
miejsca x
i y
sortować, jest dla mnie niejasne, ale ponieważ wyrażenie w nawiasie ma pasować do elementu zestawiającego, sugeruje to, że bash
zachowanie jest oczekiwane.
W każdym razie przypuszczam, że [⅕-⅕]
przynajmniej [⅕-⅖]
powinienem się zgadzać ■
.
Zauważysz, że różne narzędzia zachowują się inaczej. ksh93 zachowuje się jak bash
, GNU grep
lub sed
nie. Niektóre inne muszle zachowują się inaczej, niektóre z nich są yash
jeszcze bardziej wadliwe.
Aby zachować spójne zachowanie, potrzebujesz ustawień regionalnych, w których wszystkie postacie sortują się inaczej. Lokalizacja C jest typowa. Jednak zestaw znaków w ustawieniach regionalnych C w większości systemów to ASCII. W systemach GNU ogólnie masz dostęp do C.UTF-8
ustawień narodowych, których można użyć zamiast tego do pracy na znaku UTF-8.
Więc:
(export LC_ALL=C.UTF-8; [[ ■ = [⅕⅖⅗] ]])
lub standardowy odpowiednik:
(export LC_ALL=C.UTF-8
case ■ in ([⅕⅖⅗]) true;; (*) false; esac)
powinien zwrócić wartość false.
Inną alternatywą byłoby ustawienie tylko LC_COLLATE
C, która działałaby na systemach GNU, ale niekoniecznie na innych, w których nie byłaby w stanie określić kolejności sortowania znaków wielobajtowych.
Jedną z lekcji jest to, że równość nie jest tak jasnym pojęciem, jakiego można by się spodziewać, jeśli chodzi o porównywanie łańcuchów. Równość może oznaczać od najostrzejszego do najmniej surowego.
- Ta sama liczba bajtów i wszystkie składniki bajtów mają tę samą wartość.
- Ta sama liczba znaków i wszystkie znaki są takie same (na przykład odnoszą się do tego samego punktu kodowego w bieżącym zestawie znaków).
- Dwa ciągi mają tę samą kolejność sortowania, co algorytm sortowania ustawień narodowych (to znaczy, że ani a <b, ani b> a nie są prawdziwe).
Teraz dla 2 lub 3, przy założeniu, że oba ciągi zawierają prawidłowe znaki. W UTF-8 i niektórych innych kodowaniach niektóre sekwencje bajtów nie tworzą prawidłowych znaków.
1 i 2 niekoniecznie są z tego powodu równoważne lub ponieważ niektóre znaki mogą mieć więcej niż jedno możliwe kodowanie. Tak jest zwykle w przypadku stanowego kodowania, takiego jak ISO-2022-JP, gdzie A
można wyrazić jako 41
lub 1b 28 42 41
( 1b 28 42
jest to sekwencja przełączania na ASCII i można wstawić tyle, ile chcesz, to nie robi różnicy), chociaż ja nie spodziewałbym się, że tego rodzaju kodowanie będzie nadal używane, a narzędzia GNU przynajmniej na ogół nie działają z nimi poprawnie.
Uważaj również, że większość programów spoza GNU nie radzi sobie z wartością 0 bajtów (znak NUL w ASCII).
To, która z tych definicji zostanie użyta, zależy od narzędzia i jego implementacji lub wersji. POSIX nie jest w tym w 100% jasny. W ustawieniach regionalnych C wszystkie 3 są równoważne. Poza tym YMMV.