Bash: expansão aritmética, expansão de parâmetros e operador vírgula

Bash: expansão aritmética, expansão de parâmetros e operador vírgula

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 3em vez de 4? (É definido ncomo 4, como eu esperava, mas definido kcomo 3.)

Em contraste, a linha bash

 n=3; k=10; echo $((n++,k=n))

saídas 4, como eu esperava. (É definido ncomo 4e ktambém 4.)

Tentei substituir n++with ++n, with n=n+1e with n=$((n+1))em ambas as linhas do bash, e todas elas geraram a mesma discrepância: k=$nrendimentos 3em todos os scripts e k=nrendimentos 4em todos eles.


Meu entendimento é que, se o valor de nfor um número inteiro (o que é), $ndeverá nter 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 $nde forma diferente ndeste 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 nem vez de $nfunciona como eu esperava. Eu só quero entender o que o bash está fazendo na versão com $ne 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 $keycontém ], por exemplo

informação relacionada