Expression entre parenthèses (sans plage) correspondant à un caractère inattendu dans bash

20

J'utilise bash sous Linux. J'obtiens un succès de l'instruction if suivante, mais cela ne devrait-il pas renvoyer un code d'échec?

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

Le carré n'égale aucun des caractères, donc je ne vois pas pourquoi j'obtiens un code de réussite.

Il est important pour moi de conserver les doubles crochets dans mon cas.

Y a-t-il une autre façon de faire une plage dans ce scénario, ou quelles autres suggestions?

TuxForLife
la source
2
Probablement une conséquence de tous ces caractères ayant un ordre de tri non défini dans vos paramètres régionaux (et donc le même tri). Voir la discussion connexe en cours au sein du groupe Austin . Changez les paramètres régionaux en C pour le corriger .
Stéphane Chazelas
1
Désolé, ce Cn'est pas le cas ici car ce ne sont pas des caractères à un octet. C.UTF-8ferait le cas échéant.
Stéphane Chazelas
11
Félicitations, vous avez réussi à invoquer Stéphane brandissant un fil Austin Group sur votre première question. Cela doit valoir au moins les ⅗ d'un internets. Ou ⅘ ou même ■ Internets, car ce sont apparemment les mêmes. Bienvenue sur Unix et Linux , et continuez à poser des questions intéressantes.
derobert

Réponses:

29

C'est une conséquence du fait que ces caractères ont le même ordre de tri.

Vous remarquerez également que

sort -u << EOF




EOF

renvoie une seule ligne.

Ou ça:

expr  = 

renvoie vrai (comme requis par POSIX).

La plupart des paramètres régionaux fournis avec les systèmes GNU ont un certain nombre de caractères (et même des séquences de caractères (séquences d'assemblage)) qui ont le même ordre de tri. Dans le cas de ces ■ ⅕⅖⅗, c'est parce que l'ordre n'est pas défini, et les caractères dont l'ordre n'est pas défini finissent par avoir le même ordre de tri dans les systèmes GNU. Il y a des caractères qui sont explicitement définis comme ayant le même ordre de tri comme Ș et Ş (bien qu'il n'y ait aucune logique ou cohérence réelle apparente (pour moi de toute façon) sur la façon dont cela est fait).

C'est la source de comportements assez surprenants et faux. J'ai soulevé la question très récemment sur la liste de diffusion du groupe Austin (le corps derrière POSIX et la spécification UNIX unique) et la discussion est toujours en cours depuis le 2015-04-03.

Dans ce cas, il [y]ne faut pas savoir si doit correspondre xxet ytrier la même chose, mais comme une expression entre crochets est censée correspondre à un élément de classement, cela suggère que le bashcomportement est attendu.

En tout cas, je suppose [⅕-⅕]ou du moins [⅕-⅖]devrait correspondre .

Vous remarquerez que différents outils se comportent différemment. ksh93 se comporte comme bash, GNU grepou sedpas. Certains autres obus ont des comportements différents, certains comme yashencore plus de buggy.

Pour avoir un comportement cohérent, vous avez besoin d'un environnement local où tous les caractères sont triés différemment. La locale C est la locale typique. Cependant, le jeu de caractères dans les paramètres régionaux C sur la plupart des systèmes est ASCII. Sur les systèmes GNU, vous avez généralement accès à un C.UTF-8environnement local qui peut être utilisé à la place pour travailler sur le caractère UTF-8.

Donc:

(export LC_ALL=C.UTF-8; [[  = [⅕⅖⅗] ]])

ou l'équivalent standard:

(export LC_ALL=C.UTF-8
 case  in ([⅕⅖⅗]) true;; (*) false; esac)

devrait retourner faux.

Une autre alternative serait de ne définir que LC_COLLATEC qui fonctionnerait sur les systèmes GNU, mais pas nécessairement sur d'autres où il pourrait ne pas spécifier l'ordre de tri des caractères multi-octets.


L'une des leçons de cela est que l' égalité n'est pas une notion aussi claire qu'on pourrait s'y attendre lorsqu'il s'agit de comparer des chaînes. L'égalité peut signifier, du plus strict au moins strict.

  1. Le même nombre d'octets et tous les constituants d'octet ont la même valeur.
  2. Le même nombre de caractères et tous les caractères sont identiques (par exemple, faites référence au même point de code dans le jeu de caractères actuel).
  3. Les deux chaînes ont le même ordre de tri selon l'algorithme de classement des paramètres régionaux (c'est-à-dire que ni a <b ni b> a n'est vrai).

Maintenant, pour 2 ou 3, cela suppose que les deux chaînes contiennent des caractères valides. En UTF-8 et certains autres encodages, certaines séquences d'octets ne forment pas de caractères valides.

1 et 2 ne sont pas nécessairement équivalents à cause de cela, ou parce que certains caractères peuvent avoir plus d'un codage possible. C'est généralement le cas des codages avec état comme ISO-2022-JP où Apeut être exprimé comme 41ou 1b 28 42 41( 1b 28 42étant la séquence pour passer en ASCII et vous pouvez en insérer autant que vous le souhaitez, cela ne fera aucune différence), bien que je ne s'attendrait pas à ce que ces types d'encodage soient toujours utilisés, et les outils GNU au moins ne fonctionnent généralement pas correctement avec eux.

Sachez également que la plupart des utilitaires non GNU ne peuvent pas gérer la valeur 0 octet (le caractère NUL en ASCII).

Laquelle de ces définitions est utilisée dépend de l'utilitaire et de sa mise en œuvre ou de sa version. POSIX n'est pas sûr à 100%. Dans les paramètres régionaux C, les 3 sont équivalents. En dehors de ce YMMV.

Stéphane Chazelas
la source
Un autre cas courant où 1 et 2 diffèrent est en Unicode avec des choses comme la combinaison de caractères.
Gilles 'SO- arrête d'être méchant'
@Gilles, la combinaison de personnages est un personnage qui leur est propre. La combinaison forme un graphe / cellule, mais est toujours formée de plusieurs caractères. é (U + 00E9) et é (e suivi de U + 0301) sont le même graphem, mais deux séquences de caractères différentes (au moins du point de vue des API POSIX). Par 1 et 2, ils seraient différents. À 3 ans, ils pourraient considérer la même chose si U + 0301 avait tous ses poids de classement définis sur "IGNORE", mais ce n'est généralement pas le cas car on veut généralement décider de l'ordre des signes diacritiques.
Stéphane Chazelas
Il est généralement souhaitable de considérer éet d'être la même chaîne, mais pas e. La notion d'ordre de classement de POSIX est rarement correcte, elle est trop fortement basée sur les caractères et ne tient pas compte des méthodes de tri les plus courantes (par exemple, les dictionnaires français n'utilisent pas un ordre lexicographique pour trier les mots: ils font un premier passage lexicographique avec des accents ignorés et puis utilisez des accents pour décider des liens).
Gilles 'SO- arrête d'être méchant'
@ Gilles, oui. C'est pourquoi je dirais que ces caractères ayant le même ordre de tri (intentionnellement) dans les paramètres régionaux de la glibc n'ont pas de sens. Le é vs é est généralement traité en faisant d'abord une transformation sur les chaînes comme la décomposition canonique (similaire à la conversion en minuscules en premier lorsque vous souhaitez effectuer un tri / correspondance insensible à la casse). Voir également le guide ICU pour une bonne référence sur le sujet.
Stéphane Chazelas
@Gilles, les poids dans l'algorithme de classement des paramètres régionaux POSIX peuvent faire le tri du dictionnaire français. Voilà comment fonctionnent les poids. Un premier passage utilise les poids primaires (où e et é (et E et É) ont les mêmes et l'accent aigu combiné est ignoré) un deuxième passage (si égal) vérifie les accents, une capitalisation du 3e passage ...
Stéphane Chazelas
-3

Vous vous trompez =et vous ==n'êtes pas le même.

Essayez ces exemples:

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
Merle
la source
1
Ce n'est pas vrai. POSIX spécifie que l'opérateur =doit être utilisé pour vérifier l'égalité. Le problème, ce sont les guillemets manquants, pas l'opérateur.
scai
1
Dit également man bashdans la [[section: "L'opérateur = est équivalent à ==."
michas
1
@scai, POSIX ne spécifie pas l' [[...]]opérateur. Et = et == sont les mêmes dans les shells où ils sont implémentés (ksh / bash / zsh) et pour la correspondance de motifs, pas l'égalité.
Stéphane Chazelas
Lors de la comparaison avec un modèle, le modèle ne doit pas être cité, sinon il est pris comme une chaîne littérale, d'où le "non" dans le premier test.
xhienne