Chances are, most shell scripts you write will require some math operations, even if it’s something as simple as incrementing a variable inside of a loop. As with everything else in Linux, there are multiple ways of accomplishing the same task. Here’s a quick look at some of the options.

### Shell Arithmetic

The simplest and most common arithmetic operation encountered in scripts is addition, such as incrementing a variable inside a loop. The three examples below are just different ways of doing the same thing: adding 1 to the variable ** j**.

j=$((j+1)) ((j=j+1)) let "j=j+1"

I usually go with the second method as the most concise. Here we have a short * while *loop incrementing variable

*and printing it.*

**j**j=0; while [ $j -lt 3 ]; do echo $((j=j+1)); done 1 2 3

Here’s an example of addition, subtraction, multiplication, exponentiation, modulo, and division:

a=17; b=3; for i in \+ \- \* \** \% /; do echo "$a $i $b = $((a $i b))"; done 17 + 3 = 20 17 - 3 = 14 17 * 3 = 51 17 ** 3 = 4913 17 % 3 = 2 17 / 3 = 5

As you can see from the last line, Bash doesn’t do floating-point operations, nor does it round up or down the remainder: it just drops it altogether.

### Floating-Point Operations

#### awk

When ignoring decimals is not an option, you have a couple of popular tools: * awk* and

*. Here’s a quick example of dividing using*

**bc***with different precision settings:*

**awk**a=17; b=3; awk -v a=$a -v b=$b 'BEGIN { printf "%.17g\n", a / b }' 5.666666666666667 a=17; b=3; awk -v a=$a -v b=$b 'BEGIN { printf "%.10g\n", a / b }' 5.666666667

While not designed as a math tool, * awk *is capable of several other operations:

int(x) -- the nearest integer to x sqrt(x) -- the positive square root of x exp(x) -- the exponential of x (e ^ x) log(x) -- the natural logarithm of positive x rand() -- a random number between zero and one srand(x) -- seed the rand() function sin(x) -- the sine of x cos(x) -- the cosine of x atan2(y,x) -- the arctangent of y/x

Here are a few examples:

a=17; b=3 awk -v a=$a -v b=$b 'BEGIN { print int(a / b) }' 5 awk -v a=$a 'BEGIN { print sqrt(a) }' 4.12311 awk -v a=$a 'BEGIN { print exp(a) }' 2.4155e+07 awk -v a=$a 'BEGIN { print log(a) }' 2.83321 awk 'BEGIN { print rand() }' 0.237788 echo | awk -v a=$a 'BEGIN{srand(a);}{print rand()}' 0.33897 awk -v a=$a 'BEGIN { print sin(a) }' -0.961397 awk -v a=$a 'BEGIN { print cos(a) }' -0.275163 awk -v a=$a -v b=$b 'BEGIN { print atan2(a,b) }' 1.39612

#### bc

While offering considerable functionality, * awk *is probably not the best choice for math from the command line. A better option is

*that is commonly used in scripts. Here are examples of addition, subtraction, multiplication, exponentiation, modulo, and division:*

**bc**a=17; b=3; for i in \+ \- \* \^ \% /; do echo "oldscale=scale;scale=2;$a $i $b"|bc; done 20 14 51 4913 .02 5.66

You probably noticed that the modulo operation returned 0.02, which is incorrect. The reason for this is the `scale=2`

option I passed to * bc*. Unfortunately, the

`scale`

should always be set to 0 when calculating modulo using *:*

**bc**echo "scale=0;$a % $b"|bc 2

Also, there seems to be an issue with the division operation as well: with the requested precision of two decimal placed (`scale=2`

), the answer should have been 5.67 and not 5.66. The problem here is that * bc *does not round a quotient up or down but truncates it instead. You can deal with this in two ways: either increase precision (higher

`scale`

value) or write a separate function, as shown below.round() { echo $(printf %.$2f $(echo "scale=$2;(((10^$2)*$1)+0.5)/(10^$2)"|bc)) }; # Round up to two decimal places round $a/$b 2 5.67

The * bc *utility also offers some advanced math libraries when using the

`-l`

flag (which also sets `scale`

to 20, so keep that in mind):s(x) The sine of x, x is in radians. c(x) The cosine of x, x is in radians. a(x) The arctangent of x, arctangent returns radians. l(x) The natural log of x. e(x) The exp function of e to the power of x. j(n,x) The bessel function of integer order n of x.

And some examples:

for i in s c a l e; do echo "scale=4;${i}($a)"|bc -l; done -.9614 -.2753 1.5120 2.8332 24154952.7535 echo "scale=4;j($b,$a)"|bc -l .1349 # Calculate pi: echo "4*a(1)" | bc -l 3.14159265358979323844 # Same as above, but rounded up to four decimal places printf "%.4f\n" $(bc -l <<< "4*a(1)") 3.1416 # Square root of 4 echo "e( l(4)/2 )" | bc -l 1.99999999999999999998 # or echo "sqrt(4)" | bc 2 # or rounded up to an integer printf "%.0f\n" $(bc -l <<< "e( l(4)/2 )") 2 # Fifth root of 4 rounded up to two decimal places printf "%.2f\n" $(bc -l <<< "e( l(4)/5 )") 1.32

### Symbolic Math

#### Maxima

Many books have been written about Maxima, so I won’t be going in too deep and skip ahead to examples. Here we’re solving a first-order ordinary differential equation du/dt=e^-t + u, where t=3 and u=-0.2

apt install maxima ... maxima (%i1) diff_eq : 'diff(u,t)- u - exp(-t);' du - t (%o1) -- - u - %e dt (%i3) general_solution : ode2(diff_eq,u,t); - 2 t %e t (%o3) u = (%c - -------) %e 2 (%i4) specific_solution : ic1(general_solution,t = 3, u = -0.2),ratprint:false; (- t) - 6 3 2 t 6 %e ((2 %e - 5) %e + 5 %e ) (%o4) u = - --------------------------------------- 10 (%i5) rhs(specific_solution),t=3,ratsimp; 1 (%o5) - - 5 (%i6) diff_eq,specific_solution,diff,ratsimp; (%o6) 0 (%i7) us : rhs(specific_solution); (- t) - 6 3 2 t 6 %e ((2 %e - 5) %e + 5 %e ) (%o7) - --------------------------------------- 10 (%i8) plot2d(us,[t,0,7],[style,[lines,5]],[ylabel," "],[xlabel,"t0 = 3, u0 = -0.2, du/dt = exp(-t) + u"],[gnuplot_term,dumb]); 0 +--------------------------------------------+ |**** + + + ****+**** + + | -1 |-+ **** +-| -2 |-+ *** +-| | *** | -3 |-+ ** +-| | ** | -4 |-+ ** +-| -5 |-+ ** +-| | ** | -6 |-+ *+-| | ** | -7 |-+ +-| | **| -8 |-+ +-| -9 |-+ +-| | + + + + + + | -10 +--------------------------------------------+ 0 1 2 3 4 5 6 7 t0 = 3, u0 = -0.2, du/dt = exp(-t) + u

#### Sage

Another popular choice for symbolic math applications is SageMath or just Sage. Just as with Maxima, I am just doing an example (the same differential equation as in the Maxima sample above) to give you a feel of it. A word of caution: this might be a 2-GB install.

apt install sagemath ... sage t = var('t') u = function('u')(t) desolve(diff(u,t)==e^(-t)+u,u,ivar=t,contrib_ode=True) 1/2*(2*_C - e^(-2*t))*e^t f=desolve(diff(u,t)==e^(-t)+u,u,ivar=t,contrib_ode=True,ics=[3,-0.2]); f -1/10*((2*e^3 - 5)*e^(2*t) + 5*e^6)*e^(-t - 6)

As far as I can tell, Sage does not support dumb terminal plotting, and I am not running a graphical desktop on my Linux box, so no plot for you, sorry. But the solution is the same, of course, and so would be the plot.

On occasion, you may need to copy/paste results between Maxima and Sage. The easiest option is to disable the 2-D output in Maxima:

(%i16) specific_solution : ic1(general_solution,t = 3, u = -0.2),ratprint:false; (- t) - 6 3 2 t 6 %e ((2 %e - 5) %e + 5 %e ) (%o16) u = - --------------------------------------- 10 (%i17) display2d:false$ (%i18) specific_solution : ic1(general_solution,t = 3, u = -0.2),ratprint:false; (%o18) u = -(%e^((-t)-6)*((2*%e^3-5)*%e^(2*t)+5*%e^6))/10 # And now you can copy/paste this solution into Sage to get the same format # as the original Sage solution produced: sage: -(e^((-t)-6)*((2*e^3-5)*e^(2*t)+5*e^6))/10 -1/10*((2*e^3 - 5)*e^(2*t) + 5*e^6)*e^(-t - 6)

If your requirements exceed the capabilities offered by any of these tools, you’re probably too advanced for me. You may have to cough up a few hundred bucks and get yourself either Maple or Mathematica – both are available for Linux.

Experienced Unix/Linux System Administrator with 20-year background in Systems Analysis, Problem Resolution and Engineering Application Support in a large distributed Unix and Windows server environment. Strong problem determination skills. Good knowledge of networking, remote diagnostic techniques, firewalls and network security. Extensive experience with engineering application and database servers, high-availability systems, high-performance computing clusters, and process automation.