Это следствие того, что символы имеют одинаковый порядок сортировки.
Вы также заметите, что
sort -u << EOF
■
⅕
⅖
⅗
EOF
возвращает только одну строку.
Или это:
expr ■ = ⅕
возвращает true (как того требует POSIX).
Большинство локалей, поставляемых с системами GNU, имеют ряд символов (и даже последовательности символов (последовательности упорядочения)), которые имеют одинаковый порядок сортировки. В случае этих it's ■ это потому, что порядок не определен, и те символы, порядок которых не определен, в конечном итоге имеют одинаковый порядок сортировки в системах GNU. Есть символы, которые явно определены как имеющие один и тот же порядок сортировки, как Ș и Ş (хотя нет никакой очевидной (для меня в любом случае) реальной логики или последовательности в том, как это делается).
Это источник довольно удивительного и поддельного поведения. Недавно я поднял этот вопрос в списке рассылки Austin group (основная часть POSIX и Single UNIX Specification), и по состоянию на 2015-04-03 обсуждение продолжается.
В этом случае вопрос о том, [y]
должно ли совпадать, x
где x
и y
сортировать то же самое, мне неясен, но так как выражение в скобках предназначено для соответствия элементу сортировки, это говорит о том, что bash
поведение ожидается.
В любом случае, я полагаю, [⅕-⅕]
или, по крайней мере, [⅕-⅖]
должны совпадать ■
.
Вы заметите, что разные инструменты ведут себя по-разному. ksh93 ведет себя как bash
GNU grep
или sed
нет. Некоторые другие оболочки имеют различное поведение, некоторые, как yash
еще более глючные.
Чтобы иметь согласованное поведение, вам нужен языковой стандарт, в котором все символы сортируются по-разному. Язык Си является типичным. Однако набор символов в локали C в большинстве систем является ASCII. В системах GNU у вас обычно есть доступ к C.UTF-8
локали, которую можно использовать вместо этого для работы с символом UTF-8.
Так:
(export LC_ALL=C.UTF-8; [[ ■ = [⅕⅖⅗] ]])
или стандартный эквивалент:
(export LC_ALL=C.UTF-8
case ■ in ([⅕⅖⅗]) true;; (*) false; esac)
должен вернуть false.
Другой альтернативой может быть установка только LC_COLLATE
на C, который будет работать в системах GNU, но не обязательно в других, где он не сможет указать порядок сортировки многобайтовых символов.
Одним из уроков этого является то, что равенство не так ясно, как можно было бы ожидать, когда речь идет о сравнении строк. Равенство может означать от самого строгого до наименее строгого.
- Одинаковое количество байтов и все байтовые составляющие имеют одинаковое значение.
- Одинаковое количество символов и все символы одинаковы (например, ссылаются на одну и ту же кодовую точку в текущей кодировке).
- Две строки имеют тот же порядок сортировки, что и в алгоритме сопоставления локали (то есть, ни a <b, ни b> a не соответствует действительности).
Теперь для 2 или 3 предполагается, что обе строки содержат допустимые символы. В UTF-8 и некоторых других кодировках некоторые последовательности байтов не образуют допустимых символов.
1 и 2 не обязательно эквивалентны из-за этого или из-за того, что некоторые символы могут иметь более одной возможной кодировки. Это, как правило, в случае кодировок с сохранением состояния, таких как ISO-2022-JP, где они A
могут быть выражены как 41
или 1b 28 42 41
( 1b 28 42
являясь последовательностью для переключения на ASCII, и вы можете вставить столько из них, сколько захотите, это не будет иметь значения), хотя я не ожидал бы, что эти типы кодирования все еще используются, и инструменты GNU, по крайней мере, обычно не работают с ними должным образом.
Также имейте в виду, что большинство не-GNU утилит не могут работать со значением 0 байт (символ NUL в ASCII).
Какое из этих определений используется, зависит от реализации или версии утилиты и утилиты. POSIX не на 100% ясно об этом. В локали C все 3 эквивалентны. За пределами этого YMMV.