Выражение в скобках (без диапазонов), совпадающее с неожиданным символом в bash

20

Я использую Bash на Linux. Я получаю успех от следующего оператора if, но разве это не должно возвращать код ошибки?

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

Квадрат НЕ равен ни одному из символов, поэтому я не понимаю, почему я получаю код успеха.

Для меня важно сохранить двойные скобки в моем случае.

Есть ли другой способ сделать диапазон в этом сценарии, или какие-либо другие предложения?

TuxForLife
источник
2
Вероятно, это следствие того, что все эти символы имеют неопределенный порядок сортировки в вашей локали (и, следовательно, сортировки одинаковы). Смотрите текущее, связанное обсуждение в группе Остина . Измените локаль на C, чтобы исправить это .
Стефан Шазелас
1
Извините, Cздесь не пойдет, так как это не однобайтовые символы. C.UTF-8сделал бы, где доступно.
Стефан Шазелас
11
Поздравляем, вам удалось вызвать Стефана, владеющего веткой Austin Group, по вашему первому вопросу. Это должно стоить как минимум ⅗ интернета. Или ⅘ или даже ■ Интернет, поскольку, видимо, те же самые. Добро пожаловать в Unix и Linux , и, пожалуйста, продолжайте приносить интересные вопросы.
Дероберт

Ответы:

29

Это следствие того, что символы имеют одинаковый порядок сортировки.

Вы также заметите, что

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 ведет себя как bashGNU 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, но не обязательно в других, где он не сможет указать порядок сортировки многобайтовых символов.


Одним из уроков этого является то, что равенство не так ясно, как можно было бы ожидать, когда речь идет о сравнении строк. Равенство может означать от самого строгого до наименее строгого.

  1. Одинаковое количество байтов и все байтовые составляющие имеют одинаковое значение.
  2. Одинаковое количество символов и все символы одинаковы (например, ссылаются на одну и ту же кодовую точку в текущей кодировке).
  3. Две строки имеют тот же порядок сортировки, что и в алгоритме сопоставления локали (то есть, ни 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.

Стефан Шазелас
источник
Другой распространенный случай, когда 1 и 2 отличаются, - это Unicode с такими вещами, как объединение символов.
Жиль "ТАК - перестань быть злым"
@Gilles, объединяющие символы являются собственными персонажами. Комбинация образует графем / ячейку, но все еще состоит из нескольких символов. é (U + 00E9) и é (e, за которым следует U + 0301) - это один и тот же граф, но две разные последовательности символов (по крайней мере, с точки зрения API POSIX). К 1 и 2 они будут другими. К 3 они могли бы считать то же самое, если бы в U + 0301 все весовые коэффициенты сопоставления были установлены на «IGNORE», но это, как правило, не тот случай, так как обычно требуется определиться с порядком диакритических знаков.
Стефан Шазелас
Обычно желательно считать éи быть одной и той же строкой, но не так e. Понятие порядка сортировки POSIX редко бывает правильным, оно слишком сильно основано на символах и не учитывает наиболее распространенные способы сортировки строк (например, французские словари не используют лексикографический порядок для сортировки слов: они делают первый лексикографический проход с игнорированием акцентов и тогда используйте акценты, чтобы решить связи).
Жиль "ТАК - перестань быть злым"
@ Жиль, да. Вот почему я бы сказал, что те символы, которые имеют одинаковый порядок сортировки (намеренно) в локалях glibc, не имеют большого смысла. Обычно к «против» обращаются путем некоторого преобразования строк сначала, например, при каноническом разложении (аналогично преобразованию в нижний регистр сначала, когда вы хотите выполнить сортировку / сопоставление без учета регистра). Смотрите также руководство ICU для некоторого хорошего справочного материала по этому вопросу.
Стефан Шазелас
@Gilles, веса в алгоритме сопоставления локалей POSIX могут выполнять сортировку по французскому словарю. Вот как работают веса. В первом проходе используются первичные веса (где e и é (и E и É) имеют одинаковые значения, а острый акцент сочетания игнорируется), второй проход (если равен) проверяет акценты, капитализация 3-го прохода ...
Стефан Шазелас
-3

Вы делаете это неправильно, =и ==не то же самое.

Попробуйте эти примеры:

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
BlackBird
источник
1
Это не правда. POSIX указывает, что оператор =должен использоваться для проверки равенства. Проблема в пропущенных кавычках, а не в операторе.
Scai
1
Также man bashв [[разделе говорится : «Оператор = эквивалентен ==.»
Михас
1
@scai, POSIX не указывает [[...]]оператор. И = и == одинаковы в оболочках, где они реализованы (ksh / bash / zsh) и для сопоставления с образцом, а не с равенством.
Стефан Шазелас
При сравнении с шаблоном шаблон не должен заключаться в кавычки, иначе он принимается как буквальная строка, поэтому в первом тесте «нет».
xhienne