179

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
0

8 Answers 8

199

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
6
  • 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
    Commented Aug 5, 2016 at 2:00
  • 1
    This only works if you know that the variable holds a valid integer. I have lots of tests in my answer stackoverflow.com/a/59781257/117471 Commented Jan 17, 2020 at 5:02
  • This is not true for shift command: "In bash, variables are treated as integer or string depending on context."
    – boctulus
    Commented May 8, 2022 at 22:38
  • You can tell bash to guarantee a variable is a number with delcare -i. For example, declare -i x; x=string; echo $x; will print 0
    – JM0
    Commented Sep 12, 2023 at 2:56
  • It doesn't work to use a VAR inside for i in {1..$var} I mean, this does NOT convert str to int as required. Solution is eval. Commented Oct 20, 2023 at 17:37
24

Other way, you can use expr

Ex:

$ version="0002"
$ expr $version + 0
2
$ expr $version + 1
3
1
  • It doesn't work to use a VAR inside for i in {1..$var} I mean, this does NOT convert str to int as required. Solution is eval. Commented Oct 20, 2023 at 17:38
21

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
2
  • 7
    In other shells without printf -v this can be achieved with command substitution: int="$(printf '%d' 123.123 2>/dev/null)" Commented Apr 13, 2018 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
    Commented Oct 31, 2021 at 5: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...
2
  • 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. Commented Aug 5, 2016 at 7:34
  • For numeric comparison targetCnt=' 0'; [[ targetCnt -ne 0 ]] would have worked Commented Jan 9, 2023 at 17:07
5

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
0
5

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
1
  • 4
    Note that you can force decimal interpretation even with a leading zero using 10#, for example $((10#$n))
    – pamphlet
    Commented Dec 28, 2021 at 20:11
0

Other way to manipulate $vars as integers (for real conversion, use eval + string version of the command) is to use let:

i=0
let "i = $i + 1"     # i now is 1
echo $i
let "i = $i + 1"     # i now is 2
echo $i
0

Many answers that manipulate as integers, but none of them ACTUALLY converted the var string to INT, as the post asks. They don't solve the problem because they do NOT transform '5' to real 5 for situations like:

for i in {1..$myvar} # number in myvar is seen as string here! 

But there is a way, it is to EVAL a string version of the command.

eval "for i in {1..$myvar}; do something; done"

You must log in to answer this question.

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