Tenho uma dúvida sobre a expansão dos parâmetros do bash, dentro de uma expressão de vírgula, dentro de uma expressão aritmética. Tenho duas afirmações que pensei que deveriam ser equivalentes, mas não são.
Por que a linha bash
n=3; k=10; echo $((n++,k=$n))
saída 3
em vez de 4
? (É definido n
como 4
, como eu esperava, mas definido k
como 3
.)
Em contraste, a linha bash
n=3; k=10; echo $((n++,k=n))
saídas 4
, como eu esperava. (É definido n
como 4
e k
também 4
.)
Tentei substituir n++
with ++n
, with n=n+1
e with n=$((n+1))
em ambas as linhas do bash, e todas elas geraram a mesma discrepância: k=$n
rendimentos 3
em todos os scripts e k=n
rendimentos 4
em todos eles.
Meu entendimento é que, se o valor de n
for um número inteiro (o que é), $n
deverá n
ter o mesmo valor dentro de uma expressão aritmética. (E esta é uma expressão aritmética por causa da (( ... ))
construção.) De acordo com o manual do bash, na seção Avaliação Aritmética, em uma expressão 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.
Então, por que o bash está tratando $n
de forma diferente n
deste exemplo e exatamente como ele está avaliando $n
?
Não consegui duplicar o problema sem usar o operador vírgula. O operador vírgula, pelo que sempre vi, calcula cada uma de suas subexpressões componentes da esquerda para a direita, incluindo todos os efeitos colaterais de cada componente quando esse componente é calculado. O valor da expressão vírgula é então o valor do último componente calculado (o mais à direita).
Esta questão surgiu no contexto de um script muito mais complicado e finalmente reduzi-a a este exemplo simples que demonstra o problema.
Então, o que estou entendendo mal sobre expansão aritmética, expansão de parâmetros ou operador vírgula?
Observe que não estou procurando uma solução alternativa, pois já tenho uma: a versão com n
em vez de $n
funciona como eu esperava. Eu só quero entender o que o bash está fazendo na versão com $n
e por que ele faz algo diferente da versão com apenas n
.
Responder1
A expansão dos parâmetros do Shell aconteceantes da expressão ser avaliada, incluindo tratamento de vírgulas:
A expressão é tratada como se estivesse entre aspas duplas, mas aspas duplas entre parênteses não são tratadas de maneira especial. Todos os tokens na expressão passam por expansão de parâmetros e variáveis, substituição de comandos e remoção de cotações. O resultado é tratado como a expressão aritmética a ser avaliada.
Você pode ver isso com
unset n
echo $((n++,k=$n))
A mensagem de erro,
bash: n++,k=: syntax error: operand expected (error token is "=")
mostra que $n
é substituído antes que toda a expressão aritmética seja tratada.
No seu caso, a expressão avaliada é
n++,k=3
Responder2
Dentro de $((...))
, a expansão é feita primeiro¹ como se estivesse entre aspas duplas, e então o resultado é avaliado.
Portanto, em n=3; k=10; echo $((n++,k=$n))
(ou n=1; echo "$((++n + $n))"
não se limita a ,
), a expressão aritmética avaliada é n++, k=3
.
Aqui você precisa de:
n=3; k=10; echo "$((n++,k=n))"
(observe também as aspas, pois a expansão aritmética está sujeita a split+glob como outras formas de expansões de palavras em shells POSIX).
¹, você encontrará algumas variações onde arrays e arrays associativos são invocados, já que alguns shells tentarão lidar $((hash[$key]++))
corretamente quando $key
contém ]
, por exemplo