835

I have tried to increment a numeric variable using both var=$var+1 and var=($var+1) without success. The variable is a number, though bash appears to be reading it as a string.

Bash version 4.2.45(1)-release (x86_64-pc-linux-gnu) on Ubuntu 13.10.

Elder Geek
  • 36,023
  • 25
  • 98
  • 183
user221744
  • 8,361
  • 3
  • 13
  • 4

9 Answers9

1243

There is more than one way to increment a variable in bash, but what you tried will not work.

You can use, for example, arithmetic expansion:

var=$((var+1))
((var=var+1))
((var+=1))
((var++))

Or you can use let:

let "var=var+1"
let "var+=1"
let "var++"

See also: https://tldp.org/LDP/abs/html/dblparens.html.

Radu Rădeanu
  • 169,590
240
var=$((var + 1))

Arithmetic in bash uses $((...)) syntax.

Radu Rădeanu
  • 169,590
Paul Tanzini
  • 3,927
  • 21
    Vastly better than the accepted answer. In just 10% as much space, you managed to provide enough examples (one is plenty - nine is overkill to the point when you're just showing off), and you provided us with enough info to know that ((...)) is the key to using arithmetic in bash. I didn't realize that just looking at the accepted answer - I thought there was a weird set of rules about order of operations or something leading to all of the parenthesis in the accepted answer. – ArtOfWarfare Jun 22 '16 at 13:56
123

Various options to increment by 1, and performance analysis

Thanks to Radu Rădeanu's answer that provides the following ways to increment a variable in bash:

var=$((var+1))
((var=var+1))
((var+=1))
((var++))
let "var=var+1"
let "var+=1" 
let "var++"

There are other ways too. For example, look in the other answers on this question.

let var++
var=$((var++))
((++var))
{
    declare -i var
    var=var+1
    var+=1
}
{
    i=0
    i=$(expr $i + 1)
}

Having so many options leads to these two questions:

  1. Is there a performance difference between them?
  2. If so which, which performs best?

Incremental performance test code:

#!/bin/bash

# To focus exclusively on the performance of each type of increment
# statement, we should exclude bash performing while loops from the
# performance measure. So, let's time individual scripts that
# increment $i in their own unique way.

# Declare i as an integer for tests 12 and 13.
echo > t12 'declare -i i; i=i+1'
echo > t13 'declare -i i; i+=1'
# Set i for test 14.
echo > t14 'i=0; i=$(expr $i + 1)'

x=100000
while ((x--)); do
    echo >> t0 'i=$((i+1))'
    echo >> t1 'i=$((i++))'
    echo >> t2 '((i=i+1))'
    echo >> t3 '((i+=1))'
    echo >> t4 '((i++))'
    echo >> t5 '((++i))'
    echo >> t6 'let "i=i+1"'
    echo >> t7 'let "i+=1"'
    echo >> t8 'let "i++"'
    echo >> t9 'let i=i+1'
    echo >> t10 'let i+=1'
    echo >> t11 'let i++'
    echo >> t12 'i=i+1'
    echo >> t13 'i+=1'
    echo >> t14 'i=$(expr $i + 1)'
done

for script in t0 t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 t13 t14; do
    line1="$(head -1 "$script")"
    printf "%-24s" "$line1"
    { time bash "$script"; } |& grep user
    # Since stderr is being piped to grep above, this will confirm
    # there are no errors from running the command:
    eval "$line1"
    rm "$script"
done

Results:

i=$((i+1))              user    0m0.992s
i=$((i++))              user    0m0.964s
((i=i+1))               user    0m0.760s
((i+=1))                user    0m0.700s
((i++))                 user    0m0.644s
((++i))                 user    0m0.556s
let "i=i+1"             user    0m1.116s
let "i+=1"              user    0m1.100s
let "i++"               user    0m1.008s
let i=i+1               user    0m0.952s
let i+=1                user    0m1.040s
let i++                 user    0m0.820s
declare -i i; i=i+1     user    0m0.528s
declare -i i; i+=1      user    0m0.492s
i=0; i=$(expr $i + 1)   user    0m5.464s

Conclusion:

It seems bash is fastest at performing i+=1 when $i is declared as an integer. let statements seem particularly slow, and expr is by far the slowest because it is not a built into bash.

Keith Reynolds
  • 429
  • 3
  • 8
  • 18
  • Apparently speed correlates with command length. I wonder whether the commands call the same functions. – MatthewRock Jan 05 '17 at 15:40
  • Not what the OP asked. – raygozag Dec 15 '19 at 18:12
  • 8
    @raygozag The op asked how to increment. My answer first list many methods. How does that not directly answer the op question? Then it provide time information for each method. All that is, is just extra information to assist the op in choosing which method he wants to use. – Keith Reynolds Dec 15 '19 at 18:33
  • It was already answered, the OP did not ask for performance evaluations, no matter how helpful you want to be, they didn't ask for it. – raygozag Dec 16 '19 at 14:09
  • 1
    I found that on Bash 3.2.57, using declare -i x and then doing x+=1 in a loop (doing other thing), x did not increment more than one time. The let and (()) methods worked fine. – Jon Spencer Jan 09 '20 at 21:59
  • 1
    @JonSpencer you might be hitting this? https://unix.stackexchange.com/questions/402750/modify-global-variable-in-while-loop – sastorsl May 27 '20 at 10:25
25

There's also this:

var=`expr $var + 1`

Take careful note of the spaces and also ` is not '

While Radu's answers, and the comments, are exhaustive and very helpful, they are bash-specific. I know you did specifically ask about bash, but I thought I'd pipe in since I found this question when I was looking to do the same thing using sh in busybox under uCLinux. This portable beyond bash.

tphelican
  • 351
12

If you declare $var as an integer, then what you tried the first time will actually work:

$ declare -i var=5
$ echo $var
5
$ var=$var+1
$ echo $var
6

Reference: Types of variables, Bash Guide for Beginners

9

There's one method missing in all the answers - bc

$ VAR=7    
$ bc <<< "$VAR+2"
9
$ echo $VAR
7
$ VAR=$( bc <<< "$VAR+1" )
$ echo $VAR
8

bc is specified by POSIX standard, so should be present on all versions of Ubuntu and POSIX-compliant systems. The <<< redirection could be altered to echo "$VAR" | bc for portability, but since the question asks about bash - it's OK to just use <<<.

muru
  • 197,895
  • 55
  • 485
  • 740
Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
6

The return code 1 issue is present for all default variants (let, (()), etc.). This often causes trouble, e.g., in scripts that use set -o errexit. Here is what I am using to prevent error code 1 from math expressions that evaluate to 0;

math() { (( "$@" )) || true; }

math a = 10, b = 10
math a++, b+=2
math c = a + b
math mod = c % 20
echo $a $b $c $mod
#11 12 23 3
Juve
  • 172
  • 2
  • 12
1

This has to be the worst way to accomplish such a simple task but I just wanted to document it for fun I guess (complete opposite of code golf).

$ var=0
$ echo $var
0
$ var="$(python -c 'print('$var'+1)')"
$ echo $var
1

or

$ var="$(printf '%s\n' $var'+1' | bc)"
$ echo $var
1

Seriously use one of the other much better choices here.

1

This is the safe bet

(( var = var + 1 ))

If the resulting value is non zero, then setting exit on error will stop Your script

set -e
var=0
(( var++ ))
echo You will never get here