6

After executing the command declare -i a=5, the command a+=2 succeeds, but the command a-=2 fails. Can someone explain this strange behavior of bash?

16

In Bash, arithmetic evaluation is done inside (( )), e.g. ((i=i+3)). From Bash's man page (man bash),

((expression))

The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION.

Both -= and += are documented in the ARITHMETIC EVALUATION section, along with = *= /= %= <<= >>= &= ^= |=, and all work as you expect if you use the arithmetic notation.

+= working without that notation is an exception described under PARAMETERS section of the manual.

When += is applied to a variable for which the integer attribute has been set, value is evaluated as an arithmetic expression and added to the variable's current value, which is also evaluated.

All in all, to get the desired behavior,

#!/bin/bash
declare -i a=5
((a+=2))
echo $a
((a-=2))
echo $a

The output is 7 and 5.

| improve this answer | |
22

Bash doesn't have an -= assignment operator in the main shell syntax (arithmetic context is different, see below). That is to say, while you can use = to assign to variables, and += to append to non-integer variables, or add to integer variables, there's no -=, *= etc. to go with them. The situation is the same in Ksh, where Bash's syntax is borrowed from (in this case, as in many others); and in Zsh, which also has similar features.

The other combined assignment operators apart from += probably would make little sense for non-integers anyway, and since regular "string" variables are the most common ones, it's probably not worth it to have those operators in the main syntax. Especially since var*=123 is also a glob, and var/=123 looks like a path. But as said, += does work for non-integers though:

$ foo=123; foo+=456; echo $foo
123456

The manual, as usual is somewhat brief on this, documenting the absence of -= only by omission. Section 3.4 Shell Parameters describes variable assignment and mentions +=, but no others.

Of course, in an arithmetic context ($(( .. )), (( .. )) etc.), all of +=, -=, *= etc. are available:

$ foo=456; (( foo -= 123 )); echo $foo
333
| improve this answer | |
  • 5
    Yes, += was added to ksh93 in ksh93j in 2000, zsh in zsh-4.1.0-dev-4 in 2002, bash in 3.1 in 2005, mksh in R40 in 2011. In mksh, += (outside of ((...))) always does concatenation, even when the variable is declared as integer. – Stéphane Chazelas Jul 17 at 10:58
4

Bash allows arithmetic evaluation implicitly when used with += operator for a variable whose attribute is set to be integer type with declare -i. Without -i, it tells the shell to perform the "append" instead of "add" operation. The -= or other operators do not have a special meaning anywhere other than, when used inside arithmetic context.

See this excerpt from GNU bash man page

When += is applied to a variable for which the integer attribute has been set, value is evaluated as an arithmetic expression and added to the variable’s current value, which is also evaluated.

declare -i var=2
var+=2
printf '%d\n' "$var"
4

Without -i

declare foo=zoo
foo+=2
printf '%s\n' "$foo"
zoo2

Now for the other operators *=, /=, %=, -=, <<=, >>=, &=, ^=,|= are all supported inside $((..))

foo=144; (( foo /= 12 )); printf '%d\n' "$foo"
12

One other behavior associated with += when used with arrays arr+=foo appends the foo string to the element at the first index, while arr+=(foo) appends a new element foo to the array at the next index available.

| improve this answer | |
  • 1
    Also note that if a is an array, a+=foo appends foo to the element of indice 0 (setting it if it was previously unset), it doesn't append a foo element to the array for which you need a+=(foo) (which adds the element at the end, not at the first free slot, remember bash arrays are sparse). That's different from zsh. – Stéphane Chazelas Jul 17 at 10:41
0

Other answers note why a-=2 doesn't work (and that ((a-=2)) will work). I thought it worth recording that a+=-2 will also work:

$ declare -i a=5
$ echo $a
5
$ a+=2
$ echo $a
7
$ a+=-2
$ echo $a
5
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.