Expansión automática de variables dentro del comando bash [[]]

13

Al desreferenciar una variable bash, debe usar $signo. Sin embargo, parece que lo siguiente está funcionando bien:

x=5
[[ x -gt 2 ]]

¿Alguien puede explicar esto?

Editar: (más información)

Lo que quiero decir es cómo y por qué el comando [[]] está desreferenciando mi variable x sin el signo $. Y sí, si x = 1, la declaración se evalúa como falsa (estado de retorno 1)

Invitado
fuente
2
¿Qué quieres decir con "trabajar bien"? ¿Y cambia su evaluación si lo x=1sigue [[ x -gt 2]]?
nohillside
Quiero decir: cómo y por qué el comando [[]] hace referencia a mi variable x sin el signo $. Y sí, si x = 1, la declaración es falsa (estado de retorno 1)
Invitado

Respuestas:

9

La razón es que -eqobliga a una evaluación aritmética de los argumentos.

Un operador aritmético: -eq, -gt, -lt, -ge, -ley -nedentro de una [[ ]](en ksh, zsh y bash) medios para expandir automáticamente los nombres de variables como en el lenguaje C, no necesita de un líder $.

  • Para la confirmación debemos buscar en el código fuente de bash. El manual no ofrece confirmación directa .

    Dentro test.cdel procesamiento de operadores aritméticos entran en esta función:

    arithcomp (s, t, op, flags)

    Donde sy tson ambos operandos. Los operandos se entregan a esta función:

    l = evalexp (s, &expok);
    r = evalexp (t, &expok);

    La función evalexpse define dentro expr.c, que tiene este encabezado:

    /* expr.c -- arithmetic expression evaluation. */

    Entonces, sí, ambos lados de un operador aritmético caen (directamente) en la evaluación de expresiones aritméticas. Directamente, sin peros, sin peros.


En la práctica, con:

 $ x=3

Ambos fallan:

 $ [[ x = 4 ]] && echo yes || echo no
 no

 $ [[ x = 3 ]] && echo yes || echo no
 no

Lo cual es correcto, xno se está expandiendo yx no es igual a un número.

Sin embargo:

 $ [[ x -eq 3 ]] && echo yes || echo no
 yes

 $ [[ x -eq 4 ]] && echo yes || echo no
 no

La variable nombrada xse expande (incluso sin un $).

Esto no sucede para un […]en zsh o bash (lo hace en ksh).


Eso es lo mismo que sucede dentro de un $((…)):

 $ echo $(( x + 7 ))
 10

Y, por favor, comprenda que esto es (muy) recursivo (excepto en guión y guión):

 $ a=b b=c c=d d=e e=f f=3
 $ echo "$(( a + 7 ))" 
 10

A 😮

Y bastante arriesgado:

 $ x='a[$(date -u)]'
 $ [[ x -eq 3 ]] && echo yes || echo no
 bash: Tue Dec  3 23:18:19 UTC 2018: syntax error in expression (error token is "Dec  3 23:18:19 UTC 2018")

El error de sintaxis podría evitarse fácilmente:

 $ a=3; x='a[$(date -u >/dev/tty; echo 0)]'

 $ [[ x -eq 3 ]] && echo yes || echo no
 Tue Dec  4 09:02:06 UTC 2018
 yes

Como dice el refrán: desinfecte su aporte

 $ [[ ${x//[^0-9]} -eq 3 ]] && echo yes || echo no
 no

fin de 😮


Tanto el (más antiguo) externo /usr/bin/test(no el incorporado test) como el aún más antiguo y también externo exprno expanden las expresiones solo enteros (y aparentemente, solo enteros decimales):

 $ /usr/bin/test "x" -eq 3
 /usr/bin/test: invalid integer x

 $ expr x + 3
 expr: non-integer argument
Isaac
fuente
Interesante. No es difícil saber cómo es esto posible : al ser [[una palabra clave, los operadores y los operandos se detectan cuando se lee el comando y no después de la expansión. De este modo [[se puede tratar -eqde una manera más inteligente que, por ejemplo, [. Pero lo que me pregunto es: ¿dónde podemos encontrar documentación sobre la lógica que utiliza bash para interpretar comandos compuestos? No me parece muy obvio y aparentemente no puedo encontrar explicaciones satisfactorias en mano info bash.
fra-san
Bash no documenta esto en ningún lugar que pueda encontrar. Hay un tipo de descripción en man ksh93 : También se permiten las siguientes comparaciones aritméticas obsoletas: exp1 -eq exp2 . Existe este texto en la testsección de los operadores aritméticos man zshbuiltins que esperan argumentos enteros en lugar de expresiones aritméticas . Lo que confirma que algunos argumentos son tratados como expresiones aritméticas por la prueba incorporada en condiciones no especificadas en esta cita. Voy a confirmo con el código fuente ... ....
Isaac
7

Los operandos de las comparaciones numéricas -eq, -gt, -lt, -ge, -ley-ne se toman como expresiones aritméticas. Con alguna limitación, todavía necesitan ser palabras de shell único.

El comportamiento de los nombres de variables en la expresión aritmética se describe en Shell Arithmetic :

Las variables de shell están permitidas como operandos; La expansión de parámetros se realiza antes de evaluar la expresión. Dentro de una expresión, las variables de shell también se pueden referenciar por nombre sin utilizar la sintaxis de expansión de parámetros. Una variable de shell que es nula o sin definir se evalúa a 0 cuando se hace referencia por nombre sin utilizar la sintaxis de expansión de parámetros.

y también:

El valor de una variable se evalúa como una expresión aritmética cuando se hace referencia a ella.

Pero en realidad no puedo encontrar la parte de la documentación donde se dice que las comparaciones numéricas toman expresiones aritméticas. No se describe en Construcciones condicionales [[, ni se describe en Expresiones condicionales bash .

Pero, por experimento, parece funcionar como se dijo anteriormente.

Entonces, cosas como esta funcionan:

a=6
[[ a -eq 6 ]] && echo y 
[[ 1+2+3 -eq 6 ]] && echo y
[[ "1 + 2 + 3" -eq 6 ]] && echo y

esto también (se evalúa el valor de la variable):

b='1 + 2 + 3'
[[ b -eq 6 ]] && echo y

Pero esto no; no es una sola palabra de shell cuando [[ .. ]]se analiza, por lo que hay un error de sintaxis en el condicional:

[[ 1 + 2 + 3 -eq 6 ]] && echo y

En otros contextos aritméticos, no es necesario que la expresión no tenga espacios en blanco. Esto se imprime 999, ya que los corchetes delimitan inequívocamente la expresión aritmética en el índice:

a[6]=999; echo ${a[1 + 2 + 3]}

Por otro lado, la =comparación es una coincidencia de patrones y no implica aritmética, ni la expansión automática de variables realizada en un contexto aritmético (Construcciones condicionales):

Cuando se utilizan los operadores ==y !=, la cadena a la derecha del operador se considera un patrón y coincide de acuerdo con las reglas que se describen a continuación en Coincidencia de patrones, como si la opción de shell extglob estuviera habilitada. El =operador es idéntico a ==.

Entonces esto es falso ya que las cadenas son obviamente diferentes:

[[ "1 + 2 + 3" = 6 ]] 

como es esto, a pesar de que los valores numéricos son los mismos:

[[ 6 = 06 ]] 

y aquí, también, las cadenas ( xy 6) se comparan, son diferentes:

x=6
[[ x = 6 ]]

Sin embargo, esto expandiría la variable, así que esto es cierto:

x=6
[[ $x = 6 ]]
ilkkachu
fuente
en realidad no puedo encontrar la parte de la documentación donde se dice que las comparaciones numéricas toman expresiones aritméticas. La confirmación está en el código .
Isaac
Lo más parecido es que la descripción de arg1 OP arg2dice que los argumentos pueden ser enteros positivos o negativos, lo que supongo que implica que se tratan como expresiones aritméticas. Confusamente, también implica que no pueden ser cero. :)
Barmar
@Barmar, ehh, cierto. Pero eso también se aplica a las comparaciones numéricas [, y allí no son expresiones aritméticas. En cambio, Bash se queja de los no enteros.
ilkkachu
@ilkkachu [es un comando externo, no tiene acceso a variables de shell. A menudo se optimiza con un comando incorporado, pero aún se comporta igual.
Barmar
@Barmar, lo que quise decir fue que la frase "Arg1 y arg2 pueden ser enteros positivos o negativos". aparece en Expresiones condicionales de Bash , y esa lista se aplica [tanto como a [[. Incluso con [, los operandos -eqy amigos son / tienen que ser enteros, por lo que esa descripción también se aplica. Tomar "debe ser enteros" para significar "se interpretan como expresiones aritméticas" no se aplica en ambos casos. (Probablemente, al menos en parte debido a [que actúa como un comando normal, como usted dice.)
ilkkachu
1

Sí, su observación es correcta, la expansión de la variable se realiza en expresiones entre corchetes dobles [[ ]], por lo que no necesita poner $delante de un nombre de variable.

Esto se establece explícitamente en el bashmanual:

[[ expresión ]]

(...) La división de palabras y la expansión del nombre de ruta no se realizan en las palabras entre [[y]]; Se realiza la expansión de tilde, la expansión de parámetros y variables, la expansión aritmética, la sustitución de comandos, la sustitución de procesos y la eliminación de comillas.

Tenga en cuenta que este no es el caso de la versión de un solo parche [ ], ya [que no es una palabra clave de shell (sintaxis), sino más bien un comando (en bash está integrado, otros shells podrían usar externo, alineado para probar).

jimmij
fuente
1
Gracias por responder. Parece que esto funciona solo para números. x = ciudad [[$ x == ciudad]] Esto no funciona sin el signo $.
Invitado
3
Parece que hay más aquí: (x=1; [[ $x = 1 ]]; echo $?)retornos 0, (x=1; [[ x = 1 ]]; echo $?)retornos 1, es decir, la expansión de parámetros no se realiza xcuando comparamos cadenas. Este comportamiento se parece a la evaluación aritmética desencadenada por la expansión aritmética, es decir, lo que sucede en (x=1; echo $((x+1))). (Acerca de la evaluación aritmética, man bashestablece que "Dentro de una expresión, las variables de shell también pueden ser referenciadas por nombre sin usar la sintaxis de expansión de parámetros).
fra-san
@ fra-san De hecho, debido a que el -gtoperador espera un número, la expresión completa se reevalúa como si estuviera dentro (()), por otro lado, ==espera cadenas, por lo que se activa la función de coincidencia de patrones. No busqué en el código fuente, pero suena razonable.
jimmij
[es un caparazón incorporado en bash.
Nizam Mohamed
1
@NizamMohamed Es una construcción, pero aún no es una palabra clave.
Kusalananda