Expressão entre colchetes (sem intervalos) que corresponde a caracteres inesperados no bash

20

Estou usando o bash no Linux. Estou obtendo sucesso com a seguinte instrução if, mas isso não deve retornar um código de falha?

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

O quadrado NÃO é igual a nenhum dos caracteres, portanto não vejo por que recebo um código de sucesso.

É importante para mim manter os colchetes duplos no meu caso.

Existe alguma outra maneira de fazer um intervalo nesse cenário ou outras sugestões?

TuxForLife
fonte
2
Provavelmente, uma conseqüência de todos esses caracteres terem uma ordem de classificação indefinida em seu código do idioma (e, portanto, a mesma). Veja a discussão relacionada em andamento no grupo Austin . Altere o código do idioma para C para corrigi-lo .
Stéphane Chazelas 03/04/2015
11
Desculpe, Cnão funcionará aqui, pois não são caracteres de byte único. C.UTF-8faria quando disponível.
Stéphane Chazelas 03/04/2015
11
Parabéns, você conseguiu convocar Stéphane usando uma discussão do Austin Group em sua primeira pergunta. Vale a pena pelo menos ⅗ de um Internets. Ou even ou mesmo ■ Internet, como aparentemente essas são as mesmas. Bem-vindo ao Unix e Linux , e continue trazendo perguntas interessantes.
derobert

Respostas:

29

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 xonde xe yclassificar 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 bashcomportamento é 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 grepou sednão. Algumas outras conchas têm comportamentos diferentes, algumas como yashbuggy 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-8có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_COLLATEC 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.

  1. O mesmo número de bytes e todos os constituintes de bytes têm o mesmo valor.
  2. 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).
  3. 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 Apodem ser expressas como 41ou 1b 28 42 41( 1b 28 42sendo 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.

Stéphane Chazelas
fonte
Outro caso comum em que 1 e 2 diferem está no Unicode com coisas como combinar caracteres.
Gilles 'SO- stop being evil'
@Gilles, combinar personagens são personagens próprios. A combinação forma um graphem / célula, mas ainda é formada por vários caracteres. é (U + 00E9) e é (e seguido por U + 0301) são o mesmo graphem, mas duas seqüências diferentes de caracteres (pelo menos do ponto de vista das APIs do POSIX). Em 1 e 2, eles seriam diferentes. Em 3, eles poderiam considerar o mesmo se o U + 0301 tivesse todos os pesos de agrupamento definidos como "IGNORE", mas esse geralmente não é o caso, pois geralmente se quer decidir a ordem dos diacríticos.
Stéphane Chazelas 03/04/2015
Geralmente é desejável considerar ée ser a mesma string, mas não e. A noção de ordem de agrupamento do POSIX raramente é correta, é muito baseada em caracteres e não é responsável pelas formas mais comuns de classificar seqüências de caracteres (por exemplo, dicionários franceses não usam uma ordem lexicográfica para classificar palavras: eles fazem uma primeira passagem lexicográfica com acentos ignorados e use acentos para decidir laços).
Gilles 'SO- stop be evil'
@ Gilles, sim. É por isso que eu diria que esses caracteres com a mesma ordem de classificação (intencionalmente) nos locais glibc fazem pouco sentido. O é vs é geralmente abordado fazendo alguma transformação nas seqüências primeiro, como decomposição canônica (semelhante à conversão para minúscula primeiro, quando você deseja fazer uma classificação / correspondência sem distinção entre maiúsculas e minúsculas). Veja também o guia da UTI para obter boas referências sobre o assunto.
Stéphane Chazelas
@Gilles, os pesos no algoritmo de agrupamento de localidades POSIX podem fazer essa classificação de dicionário em francês. É assim que os pesos funcionam. Uma primeira passagem usa os pesos primários (onde e e é (e E e É) têm o mesmo e o sotaque agudo combinado é ignorado) uma segunda passagem (se igual) verifica os acentos, uma capitalização da terceira passagem ...
Stéphane Chazelas
-3

Você está fazendo errado, =e ==não é o mesmo.

Experimente estes exemplos:

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
Passaro preto
fonte
11
Isso não é verdade. POSIX especifica que o operador =deve ser usado para verificar a igualdade. O problema são as cotações ausentes, não o operador.
Scai
11
Também man bashdiz na [[seção: "O operador = é equivalente a ==."
Michas
11
@scai, POSIX não especifica o [[...]]operador. E = e == são os mesmos nos shells em que foram implementados (ksh / bash / zsh) e para correspondência de padrões, não igualdade.
Stéphane Chazelas 03/04/2015
Ao comparar com um padrão, o padrão não deve ser citado; caso contrário, é tomado como uma string literal; portanto, o "não" no primeiro teste.
xhienne