169

Trying to figure out how to convert an argument to an integer to perform arithmetic on, and then print it out, say for addOne.sh:

echo $1 + 1
>>sh addOne.sh 1
prints 1 + 1
cuonglm
  • 150,973
  • 38
  • 327
  • 406
user135986
  • 1,699
  • 2
  • 10
  • 3

6 Answers6

192

In bash, one does not "convert an argument to an integer to perform arithmetic". In bash, variables are treated as integer or string depending on context.

(If you are using a variable in an integer context, then, obviously, the variable better contain a string that looks like a valid integer. Otherwise, you'll get an error.)

To perform arithmetic, you should invoke the arithmetic expansion operator $((...)). For example:

$ a=2
$ echo "$a + 1"
2 + 1
$ echo "$(($a + 1))"
3

or generally preferred:

$ echo "$((a + 1))"
3

You should be aware that bash (as opposed to ksh93, zsh or yash) only performs integer arithmetic. If you have floating point numbers (numbers with decimals), then there are other tools to assist. For example, use bc:

$ b=3.14
$ echo "$(($b + 1))"
bash: 3.14 + 1: syntax error: invalid arithmetic operator (error token is ".14 + 1")
$ echo "$b + 1" | bc -l
4.14

Or you can use a shell with floating point arithmetic support instead of bash:

zsh> echo $((3.14 + 1))
4.14
John1024
  • 73,527
  • 11
  • 167
  • 163
  • 4
    Using $(( )) or (( )) is unsafe if you don't know what the strings are (like user input). Consider this: foo=foo (( foo += 0 )) This will crash the script as it tries to recursively evaluate foo. Same with: foo=foo foo=$(( foo + 0 )) – art Aug 05 '16 at 02:00
  • 1
    This only works if you know that the variable holds a valid integer. I have lots of tests in my answer https://stackoverflow.com/a/59781257/117471 – Bruno Bronosky Jan 17 '20 at 05:02
  • This is not true for shift command: "In bash, variables are treated as integer or string depending on context." – boctulus May 08 '22 at 22:38
25

Other way, you can use expr

Ex:

$ version="0002"
$ expr $version + 0
2
$ expr $version + 1
3
Stephen Rauch
  • 4,209
  • 14
  • 22
  • 32
Nam
  • 251
  • 3
  • 2
19

In bash, you can perform the converting from anything to integer using printf -v:

printf -v int '%d\n' "$1" 2>/dev/null

Floating number will be converted to integer, while anything are not look like a number will be converted to 0. Exponentiation will be truncated to the number before e

Example:

$ printf -v int '%d\n' 123.123 2>/dev/null
$ printf '%d\n' "$int"
123
$ printf -v int '%d\n' abc 2>/dev/null
$ printf '%d\n' "$int"
0
$ printf -v int '%d\n' 1e10 2>/dev/null
$ printf '%d\n' "$int"
1
cuonglm
  • 150,973
  • 38
  • 327
  • 406
  • 6
    In other shells without `printf -v` this can be achieved with command substitution: `int="$(printf '%d' 123.123 2>/dev/null)"` – Adrian Günter Apr 13 '18 at 19:11
  • 2
    Re: "Floating number will be converted to integer", passing anything other than `int` is undefined (that's why there's an error message that needed to be redirected to stderr, even as it appears to truncate at the decimal), so instead, just format as float with zero decimals, eg, `printf '%.0f' 123.123` results in `123` without any error message. (Obviously works for integer arguments as well.) – michael Oct 31 '21 at 05:53
17

A similar situation came up recently when developing bash scripts to run in both Linux and OSX environments. The result of one command in OSX returned a string containing the result code; i.e., " 0". Of course, this failed to test correctly in the following case:

if [[ $targetCnt != 0 ]]; then...

The solution was to force (i.e., 'convert') the result to an integer, similar to what @John1024 answered above so that it works as expected (Note integer comparison as opposed to string comparison):

targetCnt=$(($targetCnt + 0))
if [[ $targetCnt -ne 0 ]]; then...
JESii
  • 458
  • 5
  • 9
  • 5
    `==` etc in `[[` (also `[` aka `test`) do string comparison. There are **different operators** for arithmetic comparison e.g. `[[ $targetcnt -ne 0 ]]`; see manpage (or info) under Conditional Expressions. To trim spaces specifically, you could use `[` with unquoted variable expansion `[ $targetcnt == 0 ]` to get default wordsplitting (NOT done in `[[`) but in general that approach leads you into danger. – dave_thompson_085 Aug 05 '16 at 07:34
  • For numeric comparison `targetCnt=' 0'; [[ targetCnt -ne 0 ]]` would have worked – roaima Jan 09 '23 at 17:07
4

According to Bash documentation, the syntax for the evaluation of an arithmetic expression is $((expression)). For instance:

$ n=1
$ echo $((n+1))
2

You can use this in a script by assigning an argument to a variable, and then using arithmetic expansion:

n=$1
echo $((n+1))

Test it out:

$ bash ./test.sh 1
2
$ bash ./test.sh 7
8
Klaatu von Schlacker
  • 3,028
  • 13
  • 15
4

Do not use $((n)) or similar (e.g., $((n + 0)) et al) if your number may have leading zero(es). Otherwise, your number will be treated as octal. See the following example:

n="057"
n=$((n + 0)
echo $n

Result:

47
AdminBee
  • 21,637
  • 21
  • 47
  • 71
Tim
  • 41
  • 1
  • 3
    Note that you can force decimal interpretation even with a leading zero using `10#`, for example `$((10#$n))` – pamphlet Dec 28 '21 at 20:11