Isso é uma conseqüência desses personagens terem a mesma ordem de classificação.
Você também notará que
sort -u << EOF
■
⅕
⅖
⅗
EOF
retorna apenas uma linha.
Ou isso:
expr ■ = ⅕
retorna verdadeiro (conforme exigido pelo POSIX).
A maioria dos locais fornecidos com sistemas GNU tem vários caracteres (e até sequências de caracteres (seqüências de intercalação)) que possuem a mesma ordem de classificação. No caso destes, é porque a ordem não está definida e os caracteres cuja ordem não está definida acabam tendo a mesma ordem de classificação nos sistemas GNU. Existem caracteres que são explicitamente definidos como tendo a mesma ordem de classificação, como Ș e Ş (embora não exista uma lógica ou consistência aparente (para mim, de qualquer maneira) real sobre como isso é feito).
Essa é a fonte de comportamentos bastante surpreendentes e falsos. Eu tenho levantado a questão muito recentemente sobre o grupo de Austin (o órgão por trás do POSIX ea lista de discussão Single UNIX Specification) e a discussão ainda está em andamento a partir de 2015-04-03.
Neste caso, se [y]
deve corresponder a x
, em que x
e y
ordenam o mesmo não está claro para mim, mas como uma expressão de colchetes deve corresponder a um elemento de intercalação, isso sugere que% o comportamentobash
é esperado.
De qualquer forma, suponho que [⅕-⅕]
ou pelo menos [⅕-⅖]
correspondam a ■
.
Você notará que diferentes ferramentas se comportam de maneira diferente. O ksh93 se comporta como bash
, GNU grep
ou sed
não. Algumas outras shells têm comportamentos diferentes, como yash
, com mais bugs.
Para ter um comportamento consistente, você precisa de uma localidade em que todos os caracteres sejam classificados de maneira diferente. O local C é o típico. No entanto, o conjunto de caracteres na localidade C na maioria dos sistemas é ASCII. Nos sistemas GNU, você geralmente tem acesso a um C.UTF-8
locale que pode ser usado para trabalhar em caracteres UTF-8.
Então:
(export LC_ALL=C.UTF-8; [[ ■ = [⅕⅖⅗] ]])
ou o equivalente padrão:
(export LC_ALL=C.UTF-8
case ■ in ([⅕⅖⅗]) true;; (*) false; esac)
deve retornar falso.
Outra alternativa seria definir apenas LC_COLLATE
para C, o que funcionaria nos sistemas GNU, mas não necessariamente em outros, onde poderia falhar em especificar a ordem de classificação do caractere de múltiplos bytes.
Uma lição disso é que igualdade não é uma noção tão clara quanto se esperaria quando se trata de comparar strings. A igualdade pode significar, do estrito ao menos estrito.
- O mesmo número de bytes e todos os componentes de byte têm o mesmo valor.
- O mesmo número de caracteres e todos os caracteres são os mesmos (por exemplo, consulte o mesmo codepoint no conjunto de caracteres atual).
- As duas cadeias têm a mesma ordem de classificação que o algoritmo de intercalação da localidade (isto é, nem uma < b nem b > a é verdadeira).
Agora, para 2 ou 3, isso pressupõe que as duas sequências contenham caracteres válidos. Em UTF-8 e algumas outras codificações, algumas seqüências de bytes não formam caracteres válidos.
1 e 2 não são necessariamente equivalentes por causa disso, ou porque alguns caracteres podem ter mais de uma codificação possível. Esse é tipicamente o caso de codificações com informações de estado como ISO-2022-JP, em que A
pode ser expresso como 41
ou 1b 28 42 41
( 1b 28 42
sendo a sequência para alternar para ASCII e você pode inserir quantos você quiser , isso não fará diferença), embora eu não espere que esses tipos de codificação ainda estejam em uso, e as ferramentas GNU, pelo menos, geralmente não funcionam adequadamente com elas.
Tenha também em atenção que a maioria dos utilitários não-GNU não consegue lidar com o valor de 0 bytes (o caracter NUL em ASCII).
Qual dessas definições é usada depende da implementação ou versão do utilitário e do utilitário. O POSIX não é 100% claro sobre isso. Na localidade C, todos os 3 são equivalentes. Fora desse YMMV.