1. Overview

Calculating numbers is often useful in our bash scripts. However, the process is not always straightforward.

In this tutorial, we will learn a few ways we can do arithmetic operations in bash.

2. Variables in bash

bash doesn’t have a type system — all variables are strings. For that reason, we can’t simply write arithmetic operations as we would do in nearly any other language. By default, they will be interpreted as operations on strings, not numbers.

2.1. Declaring Variables

Let’s start by declaring a variable using the declare command without any attributes:

$ declare A=2+2
$ echo $A
2+2

As we can see, the string-based type system has treated this as the declaration of some text.

2.2. Integer Attribute

If we want this expression to be interpreted as arithmetic, we need to state that explicitly. One way is to declare the variable with the -i attribute:

$ declare -i A=2+2
$ echo $A
4

We should not forget that the variable is still a string. The -i flag states that any expressions will be parsed as integers on assignment to this variable.

If there’s a parsing error, the result will default to zero. Similarly, it will drop any fractional part of the number:

$ A=test
$ echo $A
0

2.3. let Command

Alternatively, the let command allows us to declare a variable and perform an arithmetic operation during the assignment. The difference here is that the variable can later be reassigned to something other than an integer:

$ let A=2+2
$ echo $A
4
$ A=test
$ echo $A
test

3. Parameter Expansion

Now that we’ve created variables, we need a way to access their values. Also, sometimes we want to get the value of an expression on-the-fly, without a declaration.

Parameter expansion allows us to substitute an expression with its value. We use it to get values from variables, invoke commands, and perform arithmetic operations.

3.1. Variable Substitution

We can access the value of the variable using the dollar sign ($):

$ A=2
$ echo $A
2

We can additionally use curly brackets to separate the variable’s name from the rest of the expression:

$ echo ${A}string
2string

3.2. Arithmetic Expansion

We can get the value of an arithmetic operation, without declaring it as a variable, by putting it in double parentheses:

$ A=2;B=2
$ echo $((A+B+1))
5

3.3. Counting Lines Example

To put this knowledge to good use, let’s write a script that will print the content of a file with the line number on the left.

We’ll start by declaring a variable for input and a variable for the line counter:

declare input=$1
declare -i counter=1

Then, we’ll add padding to the line number to ensure that its length is constant. To make this happen, we’ll count the number of lines in the file using the wc command and then check the length of the resulting number to show us the pad size:

declare -i lines=`wc -l < $input`
declare -i pad=${#lines}

Here, we stored the results of the wc command inside lines by enclosing the command in the backticks. We then assigned pad by using both parameter expansion with the ${} syntax and the hash operator # before lines, which returns the length of its value.

Now, we need to iterate through lines of the file, print the counter with padding, and print the line. Finally, we’ll increment the counter with the increment operator (++) inside arithmetic expansion:

while IFS= read -r line
do
  printf "%+${pad}s" $counter
  echo "| $line"
  ((counter++))
done < "$input"

Here’s the complete script:

#!/bin/bash
declare input=$1
declare -i counter=1
declare -i lines=`wc -l < $input`
declare -i pad=${#lines}

while IFS= read line
do
  printf "%+${pad}s" $counter
  echo "| $line"
  ((counter++))
done < "$input"

Let’s try it by running its first four lines through itself. First, we’ll redirect the first four lines to a temporary file:

$ head -4 lines_padding.sh > tmp

And then run the script:

$ ./lines_padding.sh tmp
1| #!/bin/sh
2| declare input=$1
3| declare -i counter=1
4| declare -i lines=`wc -l < $input`

4. expr Command

Instead of using the built-in features of bash, we can use the external expr command. It’s not the most powerful program to do the job, but it’s widely used. Also, it’s part of the POSIX standard. expr parses arithmetic expressions in addition to boolean expressions. It can even perform some matching and substring operations on strings:

$ expr 2 + 3
5
$ expr 2 \< 3
1
$ expr substr Baeldung 1 4
Bael

We should note that many characters like “<” will need escaping. Escaping is not needed when using built-in bash features. Also, the BSD/macOS version of expr is limited to just arithmetic and boolean operations.

5. bc Command

The bc command is similar to expr but is much more powerful. It’s also part of the POSIX standard. Most importantly, bc allows for floating-point operations. Additionally, it uses a wider array of operators and provides a simple scripting language.

5.1. Floating-Point Operations

If we define a scale (the maximum number of digits after the decimal point), we can perform floating-point operations with arbitrary precision:

$ echo "scale=2;4/3" | bc
1.33

5.2. Scripting Features

bc can also parse a simple, C-like scripting language that allows us to declare variables, write loops, and write conditional statements. Let’s write a script that will print even numbers from range 1 to 10:

$ echo "for(i=1; i<=10; i++) {if (i % 2 == 0) i;}" | bc
2
4
6
8
10

5.3. Built-in Functions

There are also some built-in mathematical functions. For example, we can get the square root of ten with a scale of four:

$ echo "scale=4;sqrt(10)" | bc
3.1622

6. Conclusion

In this article, we first saw how to use the internal features of bash to perform numeric processing.

Then we saw some additional commands that we can call from our bash scripts to do more complex processing.

We tried a few use cases for performing arithmetic operations as well as storing and printing their results.