One thing that often confuses new users to the Unix / Linux shell, is how to do (even very simple) maths. In most languages, `x = x + 1`

(or even `x++`

) does exactly what you would expect. The Unix shell is different, however. It doesn’t have any built-in mathematical operators for variables. It can do comparisons, but maths isn’t supported, not even simple addition.

Following the Unix tradition (“do one thing, and do it well”) to the extreme, because the `expr`

*and* `bc`

utilites can do maths, there is absolutely no need for `sh`

to re-invent the wheel.

Yes, I agree. This is frustrating. If I’ve got one gripe against shell programming, then this is it.

## Addition and Subtraction

So how do we cope? There are basically two ways, depending on whether we choose `expr`

or `bc`

:

#!/bin/sh
echo "Give me a number: "
read x
echo "Give me another number: "
read y
###### Here's where we have the two options:
# The expr method:
exprans=`expr $x + $y`
# The bc method:
bcans=`echo $x + $y | bc`
###### Did you see the difference?
echo "According to expr, $x + $y = $exprans"
echo "According to bc, $x + $y = $bcans"

As you can see, the language is slightly different for the two commands; `expr`

parses an expression passed to it as arguments: `expr something function something`

whereas `bc`

takes the expression as its input (`stdin`

): `echo something function something | bc`

. Also, for `expr`

, you must put spaces around the arguments: “`expr 1+2`

” doesn’t work. “`expr 1 + 2`

” works.

## Multiplication

Multiplication is a little awkward, too; the `*`

asterisk, which traditionally denotes multiplication, is a special character to the shell; it means “every file in the current directory”, so we have to delimit it with a backslash. “`*`

” becomes “`\*`

“:

#!/bin/sh
echo "Give me a number: "
read x
echo "Give me another number: "
read y
###### Here's where we have the two options:
# The expr method:
exprans=`expr $x \\* $y`
# The bc method:
bcans=`echo $x \\* $y | bc`
###### Did you see the difference?
echo "According to expr, $x * $y = $exprans"
echo "According to bc, $x * $y = $bcans"

The other thing to note here, is the backtick (```

). This grabs the output of the command it surrounds, and passes it back to the caller. So a command like

x=`expr 1 + 2`

means that, while if you type `expr 1 + 2`

at the command line, you’d get “`3`

” back:

steve@nixshell$ expr 1 + 2
3
steve@nixshell$

If you enclose it with backticks, then the variable `$x`

becomes set to the output of the command. Therefore,

x=`expr 1 + 2`

Is equivalent to (but of course more flexible than):

x=3

One last thing about assigning values to variables: Whitespace MATTERS. Don’t put spaces around the `=`

sign. “`x = 3`

” won’t work. “`x=3`

” works.

## Update: 17 Feb 2007 : Division, and Base Conversion

As noted by Constantin, the “scale=x” function can be useful for defining precision (bc sometimes seems to downgrade your precision: “`echo 5121 / 1024 | bc`

” claims that the answer is “5”, which isn’t quite true; 5120/1024=5. `echo "scale = 5 ; 5121 / 1024" | bc`

produces an answer to 5 decimal places (5.00097)).

Another important note I would like to add, is that `bc`

is great at converting between bases:

### Convert Decimal to Hexadecimal

steve@nixshell$ **bc**
**obase=16**
**12345**
3039

This tells us that 12345 is represented, in Hex (Base 16) as “0x3039”.

Similarly, we can convert back to decimal (well, we can use bc to convert any base to any other base)…

steve@nixshell$ **bc**
**ibase=16
3039**
12345

Or we can convert from Binary:

steve@nixshell$ **bc
ibase=2
01010110**
86
steve@nixshell$

… which tells us that 01010110 (Binary) is 86 in decimal. We can get that in hex, like this:

steve@nixshell$ **bc
obase=16
ibase=2
01010110**
56
steve@nixshell$

Which tells `bc`

that the input base is 2 (Binary), and the output base is 16 (Hex). So, 01010110 (base 2) = 56 (hex) = 86 (decimal).

Note that the order does matter a lot; if we’d have said “ibase=2; obase=16”, that would be interpreted differently from “obase=16; ibase=2”.

I hope that this article will help some people out with some of the more frustrating aspects of shell programming. Please, let me know what you think.