Shell Arithmetic


The default Linux shell, bash, is an advanced command interpreter that allows a system administrator to program (or 'script') complex system administration tasks. All common program control mechanisms are supported : if-then-else statements, case statements, iterations (loops), and so on. The bash shell also has arithmetic operators (add, subtract, divide, multiply), so you can use bash scripts to do calculations. Here are some examples.

Savings plan

This is a classic programming exercise : Say you have a given amount of money. You put it in the bank, and receive an annual interest. This interest is added to the initial amount every year. How much money will you have in 5 years ?

a possible solution :

#!/bin/bash

YEARS="5"
AMOUNT="15000"
RATE="2"		##interest rate in %

echo "SAVINGS PLAN"
echo "	YEARS		INTEREST	TOTAL"

for i in $(seq 1 1 $YEARS) ; do
	let INTEREST=$(( $AMOUNT * $RATE / 100 ))
	echo -n "	$i		$INTEREST		"

	let AMOUNT=$(( $AMOUNT + $INTEREST  ))
	echo "$AMOUNT"
done

# display and wait for any key (for run from GUI)
read dummy;
	

Loan Calculator

Say a friend wants to borrow a considerable amount of money, and is willing to pay you a fair interest. You could simply calculate how much interest this amount would give you if you left it in your savings account (see previous example) and charge that to your friend. However, you'd be charging interest upon interest. Also, as your friend pays back a small amount regularly, the amount over which you calculate the interest should decrease over time. Here's an attempt to handle that programmatically. In stead of hard-coded values in the script, we've also added interactive input.

#!/bin/bash
echo;echo;echo	#just some whitespace
echo "SILLY LOAN CALCULATOR"; echo;

## init
AMOUNT=""		# amount of loan
RATE=""			# interest rate
YEARS=""		# period of loan

## get input
while [[ "$AMOUNT" = "" ]]; do
	echo -n "amount of the loan: "
	read AMOUNT;
done
while [[ "$RATE" = "" ]]; do
	echo -n "interest rate:	"
	read RATE;
done
while [[ "$YEARS" = "" ]]; do
	echo -n "payback period in years: "
	read YEARS;
done

## values for internal use
INTEREST="0"
SUMINTEREST="0"
SALDO="$AMOUNT"
let MONTHS=$(( 12 * $YEARS ))
let AMOUNT_PER_YEAR=$(( $AMOUNT / $YEARS  ))

## main 
echo "TERUGBETALING LENING"
echo "	#YEARS		SUM		INTEREST"

for i in $(seq 1 1 $YEARS) ; do
	# calculate
	let INTEREST=$(( $AMOUNT_PER_YEAR * $RATE * $i / 100 ))
	let SUMINTEREST=$(( $SUMINTEREST + $INTEREST  ))
	
	# report
	echo "	$i		$SALDO		$INTEREST "

	# set values for next interation
	let SALDO=$(( $SALDO - $AMOUNT_PER_YEAR  ))
done

# Total amount due
let TOTAL=$(( $AMOUNT + $SUMINTEREST ))


# Output
echo; echo; echo "SOLUTIONS:"
echo "Amount borrowed:	$AMOUNT"
echo "Period:	 $YEARS years ($MONTHS months)"
echo "interest:	 $SUMINTEREST ($RATE % per year)"
echo;echo;
echo "solution 1 : "
echo "	monthly payback:	$MONTHS months x $(( $AMOUNT / $MONTHS )) euro = $AMOUNT euro"
echo "	+ interest to be paid separately : $SUMINTEREST euro"
echo;echo;
echo "solution 2  : "
echo "	total amount due (including interest) : $AMOUNT + $SUMINTEREST = 		$TOTAL euro"
echo "	monthly payback:	 $MONTHS maanden x $(( $TOTAL / $MONTHS )) euro = 	$TOTAL euro"
echo;echo;

# display and wait (run from GUI)
read dummy;
	

Loan Calculator - with maximum monthly packback

The same Loan Calculator, but with some bells and whistles : this one allows the user to specify a maximum payback amount per month, and the lenght of the loan will be calculated accordingly. Once the lengt is established, the loan is (re-)calculated again as in the previous example.

#!/bin/bash

echo;echo;echo	#just some whitespace
echo "SILLY LOAN CALCULATOR"; echo;

## init
AMOUNT=""		# amount of loan
RATE=""			# interest rate
MAX_PER_MONTH=""	# maximum payback amount per month

PAYBACK="65353"		# payback per month
MONTHS="0"		# number of months
INTEREST="0"		# interest amount per year
SUMINTEREST="0"		# cumulative interest over the years
TOTAL="0"		# borrowed amount + cumulative interest
AMOUNT_PER_YEAR="0"	

## get input
while [[ "$AMOUNT" = "" ]]; do
	echo -n "amount of the loan: "
	read AMOUNT;
done
while [[ "$RATE" = "" ]]; do
	echo -n "interest rate:	"
	read RATE;
done
while [[ "$MAX_PER_MONTH" = "" ]]; do
	echo -n "maximum payback per month: "
	read MAX_PER_MONTH;
done

## calculate loans until PAYBACK approaches MAX_PER_MONTH
let MONTHS=$(( $AMOUNT / $MAX_PER_MONTH ))
while [[ "$PAYBACK" -gt "$MAX_PER_MONTH" ]]; do
	let MONTHS=$(( $MONTHS + 1 ))
	
	if [[  "$(( $MONTHS % 12 ))" -eq "0" ]] ; then
		let YEARS=$(( $MONTHS / 12 ))
	else
		let YEARS=$(( $MONTHS / 12 + 1 ))
	fi

	let AMOUNT_PER_YEAR=$(( $AMOUNT / $MONTHS / 12 ))
	for i in $(seq 1 1 $YEARS) ; do
		let INTEREST=$(( $AMOUNT_PER_YEAR * $RATE * $i / 100 ))
		let SUMINTEREST=$(( $SUMINTEREST + $INTEREST  ))
	done
	
	let TOTAL=$(( $AMOUNT + $SUMINTEREST ))
	let PAYBACK=$(( $TOTAL / $MONTHS  ))
done

## recalculate a payback plan with length (in months) found previously
[[  "$(( $MONTHS % 12 ))" -eq "0" ]] && \
	let YEARS=$(( $MONTHS / 12 )) || \
	let YEARS=$(( $MONTHS / 12 + 1 ))

let AMOUNT_PER_YEAR=$(( $AMOUNT / $YEARS ))
SALDO="$AMOUNT"	

echo;echo "LOAN PAYBACK PLAN"
echo "	YEARS		SALDO		INTEREST "

for i in $(seq 1 1 $YEARS) ; do
	# calculate
	let INTEREST=$(( $AMOUNT_PER_YEAR * $RATE * $i / 100 ))
	let SUMINTEREST=$(( $SUMINTEREST + $INTEREST  ))
	
	# report
	echo "	$i		$SALDO		$INTEREST "

	# set values for next interation
	let SALDO=$(( $SALDO - $AMOUNT_PER_YEAR  ))
done
let TOTAL=$(( $AMOUNT + $SUMINTEREST ))

#output
echo; echo; echo "OPLOSSING:"
echo "amount:	$AMOUNT euro"
echo "lenght: 	$MONTHS months ($YEARS years)"
echo "interest:	$SUMINTEREST euro ($RATE % per jaar)"
echo
echo "	total payback : $AMOUNT + $SUMINTEREST = 		$TOTAL euro"
echo "	payback: $MONTHS maanden x $(( $TOTAL / $MONTHS )) euro = 	$TOTAL euro"
echo 
echo
	

real vs. integer values

Note that the bash shell calculates with integers. This means that real numbers (eg. the results of a division) are rounded to the lower integer. To enable decimal fractions, you might use bc, an arbitrary precision (floating point) calculator for unix shells, or apply this silly workaround.

Say you want to calculate an interest rate of 2.65 percent over an amount $AMOUNT. You could do something like

AMOUNT=10000
RATE=265       # per 10,000 -  ie 2.65 % 

# multiply relevant values by 100 to compansate for 'increased' %
let AMOUNT=$(( $AMOUNT * 100 ))			
let INTEREST=$(($AMOUNT * $RATE / 100  ))

# in the end, divide by 100 to get original values back
let AMOUNT=$(( $AMOUNT / 100 ))
let INTEREST=$(($INTEREST / 100 ))
	

Note that this workaround makes your script more complex, and your calculations more error prone. The precision is (only) 2 decimals.

References


Koen Noens
December 2007

The Silly Software Company Silly Software Company
-=oOo=-


A poor man's way of doing things
is still a way to get things done