Solía confiar en el hecho de que citar cadenas siempre es una buena práctica para evitar que el shell lo analice.
Entonces me encontré con esto:
$ x='('
$ [ "$x" = '1' -a "$y" = '1' ]
bash: [: `)' expected, found 1
Intentando aislar el problema, obteniendo el mismo error:
$ [ '(' = '1' -a '1' = '1' ]
bash: [: `)' expected, found 1
Resolví el problema así:
[ "$x" = '1' ] && [ "$y" = '1' ]
Todavía necesito saber qué está pasando aquí.
[[ "$x" = '1' && "$y" = '1' ]]
-a
y-o
es obsoleta por este motivo (que es lo que significa el[OB]
superíndice al lado de su especificación). Si escribiste en su[ "$x" = 1 ] && [ "$y" = 1 ]
lugar, estarías bien y estarías dentro del ámbito del comportamiento bien definido / estandarizado.[ "x$x" = "x1" ]
para evitar que los argumentos se malinterpreten como operadores.dash
lugar de Bash, sigue siendo una práctica útil.Respuestas:
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:
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 dex
, 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,
salidas
yes
, y obviamente es una expresión válida; pero, si combinamos los dos,Bash rechaza la expresión si y solo si
x
es(
o!
.Si tuviéramos que ejecutar lo anterior utilizando el
[
programa real , es decirel 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 dex
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 menudox
, delante de las cadenas (se comparan ambos valores), para garantizar que la expresión se reconozca como una comparación de cadenas:fuente
[
error incorporado. En todo caso, es un defecto de diseño inherente.[[
es una palabra clave de shell , no solo un comando incorporado, por lo que puede ver las cosas antes de eliminar las comillas y, de hecho, anula la división de palabras habitual. por ejemplo[[ $x == 1 ]]
, no necesita usar"$x"
, porque un[[
contexto es diferente de lo normal. De todos modos, así es como[[
se pueden evitar las trampas[
. POSIX requiere[
que se comporte de la manera que lo hace, y bash es en su mayoría compatible con POSIX incluso sin él--posix
, por lo que cambiar[
a una palabra clave no es atractivo.[
.[
el comportamiento está cargado por la historia anterior a POSIX, elegí la última palabra en su lugar.) Personalmente evito usar[[
en mis guiones de ejemplo, pero solo porque es un excepción a la recomendación de citas que siempre insisto (dado que omitir citas es la razón más común para los errores de script que veo), y no he pensado en un párrafo simple para explicar por qué[[
es una excepción a las reglas, sin hacer mi recomendación de cita sospechar.sh
scripts, que es anterior a la estandarización POSIX. Usando subexpresiones entre paréntesis y-a
y-o
operadores lógicos en las pruebas /[
es más eficiente que el confiar en el encadenamiento de la expresión (a través de&&
y||
); Es solo que en las máquinas actuales, la diferencia es irrelevante.&&
y||
, pero la razón es que nos facilita a los humanos mantenerlas (leerlas, comprenderlas y modificarlas si es necesario) sin introducir errores. Por lo tanto, no estoy criticando su sugerencia, sino solo el razonamiento detrás de la sugerencia. (Por razones muy similares, la.LT.
,.GE.
, etc. operadores de comparación en Fortran 77 tiene versiones mucho más humanos ambiente<
,>=
, etc., en las versiones posteriores.)[
akatest
ve:test
acepta subexpresiones entre paréntesis; entonces piensa que el paréntesis izquierdo abre una subexpresión y está tratando de analizarlo; el analizador ve=
como lo primero en la subexpresión y piensa que es una prueba de longitud de cadena implícita, por lo que es feliz; la subexpresión debería ir seguida de un paréntesis derecho y, en su lugar, el analizador encuentra en1
lugar de)
. Y se queja.Cuando
test
tiene exactamente tres argumentos, y el argumento del medio es uno de los operadores reconocidos, aplica ese operador a los argumentos primero y tercero sin buscar subexpresiones entre paréntesis.Para ver todos los detalles
man bash
, busquetest expr
.Conclusión: el algoritmo de análisis utilizado por
test
es complicado. Use solo expresiones simples y use los operadores de shell!
,&&
y||
para combinarlos.fuente