Este es un caso de esquina muy oscuro que uno podría considerar un error en cómo [
se define la prueba incorporada; sin embargo, coincide con el comportamiento del [
binario real disponible en muchos sistemas. Por lo que yo puedo decir, que sólo afecta a ciertos casos y una variable que tiene un valor que coincide con un [
operador como (
, !
, =
, -e
, y así sucesivamente.
Permítanme explicar por qué y cómo solucionarlo en los shells Bash y POSIX.
Explicación:
Considera lo siguiente:
x="("
[ "$x" = "(" ] && echo yes || echo no
No hay problema; lo anterior no produce ningún error, y las salidas yes
. Así es como esperamos que las cosas funcionen. Puede cambiar la cadena de comparación a '1'
si lo desea, y el valor de x
, y funcionará como se espera.
Tenga en cuenta que el /usr/bin/[
binario real se comporta de la misma manera. Si ejecuta, por ejemplo, '/usr/bin/[' '(' = '(' ']'
no hay error, porque el programa puede detectar que los argumentos consisten en una sola operación de comparación de cadenas.
El error ocurre cuando nosotros y con una segunda expresión. No importa cuál sea la segunda expresión, siempre que sea válida. Por ejemplo,
[ '1' = '1' ] && echo yes || echo no
salidas yes
, y obviamente es una expresión válida; pero, si combinamos los dos,
[ "$x" = "(" -a '1' = '1' ] && echo yes || echo no
Bash rechaza la expresión si y solo si x
es (
o !
.
Si tuviéramos que ejecutar lo anterior utilizando el [
programa real , es decir
'/usr/bin/[' "$x" = "(" -a '1' = '1' ] && echo yes || echo no
el error sería comprensible: dado que el shell realiza las sustituciones variables, el /usr/bin/[
binario solo recibe parámetros (
=
(
-a
1
=
1
y la terminación ]
, es comprensible que no pueda analizar si los paréntesis abiertos comienzan una sub-expresión o no, habiendo un y la operación en cuestión. Claro, analizarlo como dos comparaciones de cadenas es posible, pero hacerlo con avidez de esa manera puede causar problemas cuando se aplica a expresiones adecuadas con subexpresiones entre paréntesis.
El problema, en realidad, es que el shell [
incorporado se comporta de la misma manera, como si expandiera el valor de x
antes de examinar la expresión.
(Estas ambigüedades, y otras relacionadas con la expansión de variables, fueron una razón importante por la que Bash implementó y ahora recomienda usar las [[ ... ]]
expresiones de prueba en su lugar).
La solución es trivial, y a menudo se ve en scripts que usan sh
shells más antiguos . Agrega un carácter "seguro", a menudo x
, delante de las cadenas (se comparan ambos valores), para garantizar que la expresión se reconozca como una comparación de cadenas:
[ "x$x" = "x(" -a "x$y" = "x1" ]
[[ "$x" = '1' && "$y" = '1' ]]