1

I have a question about bash's parameter expansion, inside a comma expression, inside an arithmetic expression. I have two statements that I thought should be equivalent, but they're not.

Why does the bash line

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

output 3 instead of 4? (It sets n to 4, as I expected, but sets k to 3.)

In contrast, the bash line

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

outputs 4, as I expected. (It sets n to 4, and k to 4 also.)

I've tried replacing n++ with ++n, with n=n+1, and with n=$((n+1)) in both bash lines, and they all yield the same discrepancy: k=$n yields 3 in all the scripts, and k=n yields 4 in all of them.


My understanding is that, if the value of n is an integer (which it is), $n and n should have the same value inside an arithmetic expression. (And this is an arithmetic expression because of the (( ... )) construct.) According to the bash manual, in the section Arithmetic Evaluation, in an arithmetic expression:

   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.

So why is bash treating $n differently from n in this example, and exactly how is it evaluating $n?


I haven't been able to duplicate the issue without using the comma operator. The comma operator, from what I've always seen, computes each of its component subexpressions from left to right, including all the side effects of each component when that component is computed. The value of the comma expression is then the value of the last component computed (the rightmost one).


This question arose in the context of a much more complicated script, and I finally narrowed it down to this simple example which demonstrates the issue.

So, what am I misunderstanding about arithmetic expansion, parameter expansion, or the comma operator?

Note that I'm not looking for a workaround, since I have one already: the version with n instead of $n works as I expected. I just want to understand what bash is doing in the version with $n, and why it does something different from the version with just n.

3
  • I think the point here is "parameter expansion is performed before the expression is evaluated". I understand this as $((n++,k=$n)) is first expanded to $((n++,k=3)) (i.e. you "force" parameter expansion) and then evaluated arithmetically, leaving n to be 4, but k still at 3. When no parameter expansion is necessary (no $ inside the expression), only the $((n++,k=n)) arithmetic evaluation is performed as per left-to-right order.
    – AdminBee
    Commented Mar 10, 2020 at 9:57
  • @AdminBee Thank you, that makes sense. It’s too late here today, but I’ll test it with some other examples tomorrow, now that I know what kind of thing to try. Commented Mar 10, 2020 at 10:00
  • Similarly, this gives an error: i=3; echo $(( $i++ )) (since 3++ doesn't make sense)
    – ilkkachu
    Commented Mar 10, 2020 at 11:09

2 Answers 2

3

Shell parameter expansion happens before the expression is evaluated, including comma handling:

The expression is treated as if it were within double quotes, but a double quote inside the parentheses is not treated specially. All tokens in the expression undergo parameter and variable expansion, command substitution, and quote removal. The result is treated as the arithmetic expression to be evaluated.

You can see this with

unset n
echo $((n++,k=$n))

The error message,

bash: n++,k=: syntax error: operand expected (error token is "=")

shows that $n is replaced before the whole arithmetic expression is handled.

In your case, the expression which is evaluated is

n++,k=3
3

Inside $((...)), expansion are done first¹ as if withing double quotes, and then the result is evaluated.

So in n=3; k=10; echo $((n++,k=$n)) (or n=1; echo "$((++n + $n))", that's not limited to ,), the arithmetic expression that is evaluated is n++, k=3.

Here, you need:

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

(also note the quotes as arithmetic expansion is subject to split+glob like other forms of word expansions in POSIX shells).


¹, you'll find some variation where arrays and associative arrays are invoked as some shells will try to handle the $((hash[$key]++)) correctly when $key contains ] for instance

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .