Wyrażenie w nawiasie (bez zakresów) pasujące do nieoczekiwanego znaku w bash


20

Używam bash na Linuksie. Odnoszę sukces z następujących instrukcji if, ale czy to nie powinno zwrócić kodu błędu?

if [[  = [⅕⅖⅗] ]] ; then echo yes ; fi

Kwadrat NIE jest równy żadnemu ze znaków, więc nie rozumiem, dlaczego dostaję kod sukcesu.

Ważne jest dla mnie zachowanie podwójnych nawiasów w moim przypadku.

Czy istnieje inny sposób wykonania zakresu w tym scenariuszu lub jakie inne sugestie?


2
Prawdopodobnie jest to konsekwencja tego, że wszystkie postacie mają nieokreślony porządek sortowania w twoich ustawieniach regionalnych (a zatem sortują to samo). Zobacz trwającą, powiązaną dyskusję w grupie Austin . Zmień ustawienia regionalne na C, aby to naprawić .
Stéphane Chazelas,

1
Przepraszamy, Cnie zrobię tego tutaj, ponieważ nie są to znaki jednobajtowe. C.UTF-8zrobiłbym tam, gdzie to możliwe.
Stéphane Chazelas,

11
Gratulacje, udało ci się przywołać Stéphane'a z wątkiem z Austin Group na swoje pierwsze pytanie. To musi być warte co najmniej ⅗ internetów. Lub ⅘ lub nawet ■ Internetowe, ponieważ najwyraźniej są takie same. Witamy w Uniksie i Linuksie i proszę o dalsze ciekawe pytania.
derobert

Odpowiedzi:


29

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 xmiejsca xi ysortować, jest dla mnie niejasne, ale ponieważ wyrażenie w nawiasie ma pasować do elementu zestawiającego, sugeruje to, że bashzachowanie 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 greplub sednie. Niektóre inne muszle zachowują się inaczej, niektóre z nich są yashjeszcze 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-8ustawień 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_COLLATEC, 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.

  1. Ta sama liczba bajtów i wszystkie składniki bajtów mają tę samą wartość.
  2. 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).
  3. 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 Amożna wyrazić jako 41lub 1b 28 42 41( 1b 28 42jest 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.


Innym częstym przypadkiem, w którym 1 i 2 różnią się, jest Unicode z takimi rzeczami jak łączenie znaków.
Gilles „SO- przestań być zły”

@Gilles, łączenie postaci jest postacią własną. Kombinacja tworzy grafhem / komórkę, ale wciąż składa się z kilku znaków. é (U + 00E9) i é (e, po których następuje U + 0301) to ten sam grafem, ale dwie różne sekwencje znaków (przynajmniej z punktu widzenia interfejsów API POSIX). W przypadku 1 i 2 byłyby inne. Do 3 mogliby uznać to za takie samo, gdyby U + 0301 miał wszystkie wagi zestawiania ustawione na „IGNORE”, ale generalnie tak nie jest, ponieważ generalnie chce się decydować o kolejności znaków diakrytycznych.
Stéphane Chazelas,

Zwykle pożądane jest rozważenie éi bycie tym samym ciągiem, ale nie e. Pojęcie uporządkowania przez POSIX rzadko ma rację, jest zbyt mocno oparte na znakach i nie bierze pod uwagę najczęstszych sposobów sortowania ciągów (np. Francuskie słowniki nie używają porządku leksykograficznego do sortowania słów: wykonują pierwsze przejście leksykograficzne z ignorowanymi akcentami i następnie użyj akcentów, aby zdecydować o powiązaniu).
Gilles „SO- przestań być zły”

@Gilles, tak. Dlatego powiedziałbym, że postacie mające tę samą kolejność sortowania (celowo) w ustawieniach glibc nie mają większego sensu. É vs é zazwyczaj rozwiązuje się, dokonując najpierw transformacji ciągów, np. Rozkład kanoniczny (podobnie jak najpierw konwersja na małe litery, jeśli chcesz wykonać sortowanie / dopasowywanie bez rozróżniania wielkości liter). Zobacz także przewodnik na OIOM-ie, aby znaleźć dobre referencje na ten temat.
Stéphane Chazelas,

@Gilles, wagi w algorytmie sortowania ustawień narodowych POSIX mogą wykonać sortowanie słowników francuskich. Tak działają ciężarki. Pierwsze przejście wykorzystuje wagi podstawowe (gdzie e i é (i E i É) mają to samo, a łączący akcent ostry jest ignorowany) drugie przejście (jeśli jest równe) sprawdza akcenty, wielkie litery trzeciego przejścia ...
wielkie

-3

Robisz to źle =i ==nie jesteś taki sam.

Spróbuj tych przykładów:

if [[ "■" == "[⅕⅖⅗]" ]] ; then echo yes ; else echo no ; fi

if [[ "1" == "1" ]] ; then echo yes ; else echo no ; fi

if [[ "■" == "■" ]] ; then echo yes ; else echo no ; fi

1
To nieprawda. POSIX określa, że ​​operator =powinien być używany do sprawdzania równości. Problemem są brakujące cytaty, a nie operator.
Scai

1
man bashMówi także w [[sekcji: „Operator = jest równoważny ==.”
michas,

1
@scai, POSIX nie określa [[...]]operatora. I = i == są takie same w powłokach, w których został zaimplementowany (ksh / bash / zsh) i dla dopasowania wzorca, a nie równości.
Stéphane Chazelas,

Porównując do wzorca, wzorzec nie może być cytowany, w przeciwnym razie jest traktowany jako ciąg dosłowny, stąd „nie” w pierwszym teście.
xhienne
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.