Tengo una pregunta sobre la expansión de parámetros de bash, dentro de una expresión de coma, dentro de una expresión aritmética. Tengo dos declaraciones que pensé que deberían ser equivalentes, pero no lo son.
¿Por qué la línea bash
n=3; k=10; echo $((n++,k=$n))
salida 3
en lugar de 4
? (Se establece n
en 4
, como esperaba, pero se establece k
en 3
).
Por el contrario, la línea bash
n=3; k=10; echo $((n++,k=n))
salidas 4
, como esperaba. (Se establece n
en 4
y k
también 4
).
Intenté reemplazar n++
con ++n
, con n=n+1
y con n=$((n+1))
en ambas líneas bash, y todas producen la misma discrepancia: k=$n
produce 3
en todos los scripts y k=n
produce 4
en todos ellos.
Tengo entendido que, si el valor de n
es un número entero (que lo es), $n
debería n
tener el mismo valor dentro de una expresión aritmética. (Y esta es una expresión aritmética debido a la (( ... ))
construcción). Según el manual de bash, en la sección Evaluación aritmética, en una expresión aritmética:
Shell variables are allowed as operands; parameter expansion is performed before the
expression is evaluated. Within an expression, shell variables may also be referenced by
name without using the parameter expansion syntax.
Entonces, ¿por qué bash se trata $n
de manera diferente a la n
de este ejemplo y exactamente cómo se evalúa $n
?
No pude duplicar el problema sin usar el operador de coma. El operador de coma, por lo que siempre he visto, calcula cada una de las subexpresiones de sus componentes de izquierda a derecha, incluidos todos los efectos secundarios de cada componente cuando se calcula ese componente. El valor de la expresión de coma es entonces el valor del último componente calculado (el que está más a la derecha).
Esta pregunta surgió en el contexto de un guión mucho más complicado y finalmente la reduje a este ejemplo simple que demuestra el problema.
Entonces, ¿qué estoy entendiendo mal acerca de la expansión aritmética, la expansión de parámetros o el operador de coma?
Tenga en cuenta que no estoy buscando una solución alternativa, ya que ya tengo una: la versión con n
en lugar de $n
funciona como esperaba. Solo quiero entender qué hace bash en la versión con $n
, y por qué hace algo diferente a la versión con solo n
.
Respuesta1
Se produce la expansión de los parámetros de Shellantes de que se evalúe la expresión, incluido el manejo de comas:
La expresión se trata como si estuviera entre comillas dobles, pero las comillas dobles entre paréntesis no se tratan de forma especial. Todos los tokens de la expresión se someten a expansión de parámetros y variables, sustitución de comandos y eliminación de comillas. El resultado se trata como la expresión aritmética a evaluar.
Puedes ver esto con
unset n
echo $((n++,k=$n))
El mensaje de error,
bash: n++,k=: syntax error: operand expected (error token is "=")
muestra que $n
se reemplaza antes de que se maneje toda la expresión aritmética.
En su caso, la expresión que se evalúa es
n++,k=3
Respuesta2
En el interior $((...))
, la expansión se realiza primero¹ como si estuviera entre comillas dobles, y luego se evalúa el resultado.
Entonces, en n=3; k=10; echo $((n++,k=$n))
(o n=1; echo "$((++n + $n))"
, eso no se limita a ,
), la expresión aritmética que se evalúa es n++, k=3
.
Aquí necesitas:
n=3; k=10; echo "$((n++,k=n))"
(también tenga en cuenta las comillas, ya que la expansión aritmética está sujeta a split+glob como otras formas de expansiones de palabras en shells POSIX).
¹, encontrará alguna variación en la que se invocan matrices y matrices asociativas, ya que algunos shells intentarán manejar $((hash[$key]++))
correctamente cuando $key
contiene ]
, por ejemplo