Expresión de paréntesis (sin rangos) que coincide con caracteres inesperados en bash

20

Estoy usando bash en Linux. Estoy obteniendo un éxito de la siguiente declaración if, pero ¿no debería esto devolver un código de error?

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

El cuadrado NO es igual a ninguno de los caracteres, por lo que no veo por qué obtengo un código de éxito.

Es importante para mí mantener los corchetes dobles en mi caso.

¿Hay alguna otra manera de hacer un rango en este escenario, o qué otras sugerencias?

TuxForLife
fuente
2
Probablemente una consecuencia de todos esos caracteres que tienen un orden de clasificación indefinido en su entorno local (y, por lo tanto, ordenan lo mismo). Vea la discusión en curso y relacionada en el grupo de Austin . Cambie la configuración regional a C para solucionarlo .
Stéphane Chazelas
1
Lo sentimos, Cno lo haré aquí, ya que no son caracteres de un solo byte. C.UTF-8haría donde esté disponible.
Stéphane Chazelas
11
Felicitaciones, lograste convocar a Stéphane empuñando un hilo del Grupo Austin en tu primera pregunta. Eso tiene que valer al menos ⅗ de un Internets. O ⅘ o incluso ■ Internets, ya que aparentemente son lo mismo. Bienvenido a Unix y Linux , y sigue trayendo preguntas interesantes.
derobert

Respuestas:

29

Esa es una consecuencia de que los personajes tienen el mismo orden de clasificación.

También notarás que

sort -u << EOF




EOF

devuelve solo una línea.

O eso:

expr  = 

devuelve verdadero (como lo requiere POSIX).

La mayoría de las configuraciones regionales que se envían con sistemas GNU tienen varios caracteres (e incluso secuencias de caracteres (secuencias de clasificación)) que tienen el mismo orden de clasificación. En el caso de esos ■ ⅕⅖⅗, es porque el orden no está definido, y aquellos caracteres cuyo orden no está definido terminan teniendo el mismo orden de clasificación en los sistemas GNU. Hay caracteres que se definen explícitamente como que tienen el mismo orden de clasificación como Ș y Ş (aunque no hay una lógica real o coherencia aparente (para mí de todos modos) sobre cómo se hace).

Esa es la fuente de comportamientos bastante sorprendentes y falsos. Recientemente he planteado el tema en la lista de correo del grupo de Austin (el cuerpo detrás de POSIX y la Especificación Única de UNIX) y la discusión aún continúa a partir del 03/04/2015.

En este caso, no [y]debería estar claro si debería coincidir con xdónde xy yordenar lo mismo, pero dado que una expresión de paréntesis debe coincidir con un elemento de clasificación, eso sugiere que bashse espera el comportamiento.

En cualquier caso, supongo [⅕-⅕]o al menos [⅕-⅖]debería coincidir .

Notarás que las diferentes herramientas se comportan de manera diferente. ksh93 se comporta como bashGNU grepo sedno. Algunos otros depósitos tienen comportamientos diferentes, a algunos les gusta yashaún más con errores.

Para tener un comportamiento consistente, necesita una configuración regional donde todos los caracteres se ordenan de manera diferente. La configuración regional C es la típica. Sin embargo, el conjunto de caracteres en la configuración regional C en la mayoría de los sistemas es ASCII. En los sistemas GNU, generalmente tiene acceso a un C.UTF-8entorno local que se puede utilizar para trabajar en el carácter UTF-8.

Entonces:

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

o el equivalente estándar:

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

debería devolver falso.

Otra alternativa sería establecer solo LC_COLLATEen C, que funcionaría en los sistemas GNU, pero no necesariamente en otros en los que podría no especificarse el orden de clasificación del carácter de varios bytes.


Una lección de eso es que la igualdad no es una noción tan clara como cabría esperar cuando se trata de comparar cadenas. Igualdad podría significar, de lo más estricto a lo menos estricto.

  1. El mismo número de bytes y todos los constituyentes de bytes tienen el mismo valor.
  2. El mismo número de caracteres y todos los caracteres son iguales (por ejemplo, consulte el mismo punto de código en el juego de caracteres actual).
  3. Las dos cadenas tienen el mismo orden de clasificación que el algoritmo de clasificación del entorno local (es decir, ni a <b ni b> a es verdadero).

Ahora, para 2 o 3, se supone que ambas cadenas contienen caracteres válidos. En UTF-8 y algunas otras codificaciones, algunas secuencias de bytes no forman caracteres válidos.

1 y 2 no son necesariamente equivalentes por eso o porque algunos caracteres pueden tener más de una codificación posible. Ese suele ser el caso de codificaciones con estado como ISO-2022-JP, donde Ase pueden expresar como 41o 1b 28 42 41( 1b 28 42siendo la secuencia para cambiar a ASCII y se pueden insertar tantas de las que desee, eso no hará la diferencia), aunque yo no esperaría que esos tipos de codificación aún estén en uso, y las herramientas GNU al menos generalmente no funcionan correctamente con ellas.

También tenga en cuenta que la mayoría de las utilidades que no son GNU no pueden manejar el valor de 0 bytes (el carácter NUL en ASCII).

Cuál de esas definiciones se usa depende de la utilidad y la implementación o versión de la utilidad. POSIX no es 100% claro en eso. En la configuración regional C, los 3 son equivalentes. Fuera de ese YMMV.

Stéphane Chazelas
fuente
Otro caso común donde 1 y 2 difieren es en Unicode con cosas como combinar caracteres.
Gilles 'SO- deja de ser malvado'
@Gilles, los personajes combinados son personajes propios. La combinación forma un gráfico / celda, pero aún está formada por varios caracteres. é (U + 00E9) y é (e seguido de U + 0301) son el mismo gráfico, pero dos secuencias de caracteres diferentes (al menos desde el punto de vista de las API POSIX). Por 1 y 2, serían diferentes. A los 3, podrían considerar lo mismo si U + 0301 tuviera todos sus pesos de intercalación establecidos en "IGNORE", pero ese no es el caso en general, ya que generalmente se desea decidir el orden de los signos diacríticos.
Stéphane Chazelas
Generalmente es deseable considerar éy ser la misma cadena, pero no e. La noción de orden de clasificación de POSIX rara vez es correcta, se basa demasiado en los caracteres y no tiene en cuenta las formas más comunes de ordenar cadenas (por ejemplo, los diccionarios franceses no usan un orden lexicográfico para ordenar palabras: hacen un primer paso lexicográfico con acentos ignorados y luego use acentos para decidir los lazos).
Gilles 'SO- deja de ser malvado'
@Gilles, sí. Es por eso que diría que los caracteres que tienen el mismo orden de clasificación (intencionalmente) en los entornos locales glibc no tienen mucho sentido. El é vs é generalmente se aborda haciendo alguna transformación en las cadenas primero, como la descomposición canónica (similar a convertir a minúsculas primero cuando desea hacer una clasificación / coincidencia sin distinción entre mayúsculas y minúsculas). Consulte también la guía de la UCI para obtener una buena referencia sobre el tema.
Stéphane Chazelas
@Gilles, las ponderaciones en el algoritmo de ordenación regional POSIX pueden hacer esa clasificación del diccionario francés. Así funcionan los pesos. Un primer pase usa los pesos primarios (donde e y é (y E y É) tienen el mismo y se ignora el acento agudo combinado) un segundo pase (si es igual) verifica los acentos, una capitalización de tercer pase ...
Stéphane Chazelas
-3

Lo estás haciendo mal =y ==no eres lo mismo.

Prueba estos ejemplos:

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
Mirlo
fuente
1
Eso no es cierto. POSIX especifica que el operador =debe usarse para verificar la igualdad. El problema son las comillas faltantes, no el operador.
scai
1
También man bashdice en la [[sección: "El operador = es equivalente a ==".
michas
1
@scai, POSIX no especifica el [[...]]operador. Y = y == son los mismos en los shells donde se implementa (ksh / bash / zsh) y para la coincidencia de patrones, no la igualdad.
Stéphane Chazelas
Cuando se compara con un patrón, el patrón no debe ser citado, de lo contrario se toma como una cadena literal, de ahí el "no" en la primera prueba.
xhienne