Isso é uma conseqüência desses caracteres terem a mesma ordem de classificação.
Você também notará que
sort -u << EOF
■
⅕
⅖
⅗
EOF
retorna apenas uma linha.
Ou aquilo:
expr ■ = ⅕
retorna true (conforme exigido pelo POSIX).
A maioria das localidades fornecidas com os sistemas GNU possui um número de caracteres (e até sequências de caracteres (sequências de intercalação)) que têm a mesma ordem de classificação. No caso desses, é 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 definidos explicitamente como tendo a mesma ordem de classificação como Ș e Ş (embora não exista (para mim, de qualquer maneira) lógica ou consistência reais de como isso é feito).
Essa é a fonte de comportamentos bastante surpreendentes e falsos. Eu levantei a questão muito recentemente na lista de discussão do grupo Austin (o corpo por trás do POSIX e da Especificação Única UNIX) e a discussão ainda está em andamento a partir de 03/04/2015.
Nesse caso, se [y]
devo combinar x
onde x
e y
classificar o mesmo não está claro para mim, mas como uma expressão de colchete deve corresponder a um elemento de intercalação, isso sugere que o bash
comportamento é esperado.
Em qualquer caso, suponho [⅕-⅕]
ou pelo menos [⅕-⅖]
devo corresponder ■
.
Você notará que ferramentas diferentes se comportam de maneira diferente. O ksh93 se comporta como bash
, GNU grep
ou sed
não. Algumas outras conchas têm comportamentos diferentes, algumas como yash
buggy ainda mais.
Para ter um comportamento consistente, você precisa de uma localidade em que todos os caracteres sejam classificados de maneira diferente. O código de idioma C é o típico. No entanto, o conjunto de caracteres no código de idioma C na maioria dos sistemas é ASCII. Nos sistemas GNU, você geralmente tem acesso a um C.UTF-8
código do idioma que pode ser usado para trabalhar com caracteres UTF-8.
Tã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 false.
Outra alternativa seria definir apenas LC_COLLATE
C que funcionaria em sistemas GNU, mas não necessariamente em outras onde poderia falhar em especificar a ordem de classificação do caractere de vários bytes.
Uma lição disso é que igualdade não é uma noção tão clara quanto seria de esperar quando se trata de comparar strings. Igualdade pode significar, do mais estrito ao menos estrito.
- O mesmo número de bytes e todos os constituintes de bytes têm o mesmo valor.
- O mesmo número de caracteres e todos os caracteres são os mesmos (por exemplo, consulte o mesmo ponto de código no conjunto de caracteres atual).
- As duas seqüências têm a mesma ordem de classificação do algoritmo de intercalação do código do idioma (ou seja, nem a <b nem b> a são verdadeiros).
Agora, para 2 ou 3, isso pressupõe que ambas as cadeias contêm caracteres válidos. No UTF-8 e em algumas outras codificações, alguma sequência de bytes não forma 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 é geralmente o caso de codificações com estado, como ISO-2022-JP, onde A
podem ser expressas como 41
ou 1b 28 42 41
( 1b 28 42
sendo a sequência para alternar para ASCII e você pode inserir quantas delas quiser, que não farão diferença), embora eu não esperaria que esses tipos de codificação ainda estivessem em uso, e as ferramentas GNU pelo menos geralmente não funcionam corretamente com elas.
Lembre-se também de que a maioria dos utilitários não-GNU não pode lidar com o valor de 0 byte (o caractere NUL em ASCII).
Qual dessas definições é usada depende do utilitário e da implementação ou versão do utilitário. O POSIX não é 100% claro nisso. No código C, todos os 3 são equivalentes. Fora desse YMMV.