Calculating with Bash

If you’re at a terminal or writing a script and need to do some quick integer calculations, Bash offers arithmetic expansion with the $((...)) syntax.

The usual arithmetic operations of addition, subtraction, multiplication, and division are supported, but the name of arithmetic expansion is a bit misleading as it also supports operations well beyond the elementary arithmetic set, including exponentiation, equality and inequality checks, and logical and bitwise NOT/AND/OR/XOR:

  • VAR++ VAR-- — post-increment, post-decrement
  • ++VAR --VAR — pre-increment, pre-decrement
  • + - — addition, subtraction
  • ** — exponentiation
  • * / % — multiplication, division, remainder
  • == != — equality, inequality
  • < > <= >= — comparison
  • ! && || — logical NOT, AND, OR
  • ~ & | ^ — bitwise NOT, AND, OR, XOR

These operators and their precedence are all the same as used in most C-style languages. Pivotally, the expansion can include variables defined elsewhere in the command or script:

$ number=1234
$ printf '%s\n' "$((number * 2))"
2468

One place this can be very useful is in calculating filesizes, since most of the time shell commands work in either bytes or potentially arbitrary disk block sizes. If you work with SI decimal prefixes, you can use powers of 10 to calculate the sizes:

kilobytes=$((bytes / 10**3))
megabytes=$((bytes / 10**6))
gigabytes=$((bytes / 10**9))

If you prefer to work with IEC binary prefixes, i.e. multiples of 1024:

kilobytes=$((bytes / 2**10))
megabytes=$((bytes / 2**20))
gigabytes=$((bytes / 2**30))

Or you could use bit shifting:

kilobytes=$((bytes >> 10))
megabytes=$((bytes >> 20))
gigabytes=$((bytes >> 30))