I want to know what exactly is the difference between

let a=1+1
a=$(expr 1 + 1 )

All 4 assign the variable a with 2, but what is the difference?

From what I found out so far, is that expr is slower because it is not an actual shell builtin. But not anything more than that.


All of these deal with arithmetic, but in different ways and the variable is created via different means. Some of these are specific to bash shells, while others aren't.

  • $((...)) is called arithmetic expansion, which is typical of the bash and ksh shells. This allows doing simple integer arithmetic, no floating point stuff though. The result of the expression replaces the expression, as in echo $((1+1)) would become echo 2
  • ((...)) is referred to as arithmetic evaluation and can be used as part of if ((...)); then or while ((...)) ; do statements. Arithmetic expansion expansion $((..)) substitutes the output of the operation and can be used to assign variables as in i=$((i+1)) but cannot be used in conditional statements.
  • $[...] is the old syntax for arithmetic expansion which is deprecated. See also. This was likely kept so that old bash scripts don't break. This didn't work in ksh93, so my guess is that this syntax is bash-specific. NOTE: spaces are very important here; don't confuse $[1+1] with stuff like [ $a -eq $b ]. The [ with spaces is known as the test command, and you typically see it in decision-making parts. It is very different in behavior and purpose.
  • let is a bash and ksh keyword which allows for variable creation with simple arithmetic evaluation. If you try to assign a string there like let a="hello world" you'll get a syntax error. Works in bash and ksh93.
  • $(...) is command substitution, where you literally take the output of a command and assign to a variable. Your command here is expr, which takes positional arguments, like expr arg1 arg2 arg3, so spaces are important. It's sort of like a small command-line calculator for integer arithmetic, plus some true/false and regex type of stuff. This is a shell-neutral command.

It's also worth noting that arithmetic expansion and command substitution are specified by POSIX standard, while let and $[...] aren't.

  • 1
    ((...)) actually can be used for assignments in bash, ksh and zsh: n=10; ((n+=10)); echo $n prints 20 and ((x=1)); echo $x prints 1. – Alexej Magura Dec 6 '19 at 17:45
  • 1
    @Alexej Thank you. Answer updated, that part removed – Sergiy Kolodyazhnyy Dec 8 '19 at 8:59
  • let command performs arithmetic evaluation and is a shell built-in.

    • Run this command and you get nothing (only evaluates):

      let 1+2
  • $(( )) is used to perform arithmetic expansion: read here

    • Run this one and you'll get an error (because of expansion):

  • $[ ] is the old syntax for arithmetic expansion:

    The old format $[expression] is deprecated and will be removed in upcoming of bash. Bash Man Page

  • expr is a binary command, if you want to do arithmetic expansion within a command subsituation you can use it:

    echo $(expr 1 + 2) 
    echo `expr 1 + 2`

Since some of the answers above specifically mention ksh93 it's worth noting that it can do floating point math, e.g.:

$ print $((1.0/3)) 

You can control the precision of the output with printf, e.g.:

$ printf "%.4f\n" $((1.0/3))

At least one argument must be specified as a floating-point number as above. If both are specified as integers then only integer math is done, e.g.:

$ print $((1/3))  

This can be helpful when you need floating point math in a shell script since you can avoid calling an external command.

  • It occurs to me that you might be interested to post an answer to Can I use fractions/decimals in a bash script? In spite of the title, I think the context makes clear that other Bourne-style shells are relevant, which is why I posted a zsh answer. Since ksh and bash are more similar to each other (in general) than either is to zsh, it seems to me that a kh93-based answer to that question could be quite useful. – Eliah Kagan Sep 28 '17 at 23:02

let doesn't work in cron. I think that's due to let having its own environment. Use $((...)) since it's POSIX. For example,

let x=1+2
echo x=$x

in cron, results in "x=", but "x=3" when run from shell.

echo x=$x

results in "x=3" in all cases.

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.