Bash-Funk “math” module
The following commands are available when this module is loaded:
License
SPDX-FileCopyrightText: © Vegard IT GmbH (https://vegardit.com)
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-calc
Usage: -calc [OPTION]... [FORMULA]...
Performs calculations using awk. See https://www.gnu.org/software/gawk/manual/html_node/Arithmetic-Ops.html.
Requirements:
+ Command 'awk' must be available.
Parameters:
FORMULA
The formula to calculate.
Options:
--round PRECISION (integer: 0-?)
Rounds the value with the given precision.
-----------------------------
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Examples:
$ -calc 1.103+1.203
2.306
$ -calc 1.103+1.203 --round 2
2.31
$ -calc 1.109-0.8 --round 2
0.31
$ -calc 1.109-0.8 --round 1
0.3
$ -calc 1.109-0.8 --round 0
0
$ -calc 2^1.2 --round 1
2.3
Implementation:
if [[ ! ${_FORMULA:-} ]]; then
"-calc: Formula is missing."
return 1
fi
local formula=""
for part in "${_FORMULA[@]}"; do
formula="$formula $part"
done
if [[ ${_round:-} ]]; then
LC_ALL=C awk "BEGIN{printf \"%.${_round}f\n\", ($formula)}"
else
LC_ALL=C awk "BEGIN{print ($formula)}"
fi
-round
Usage: -round [OPTION]... VALUE PRECISION
Rounds the given decimal value using 'printf' builtin..
Parameters:
VALUE (required, pattern: "[-+]?[0-9]*\.?[0-9]+")
The first number.
PRECISION (required, integer: 0-?)
Number of decimal digits.
Options:
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Examples:
$ -round 1.903 3
1.903
$ -round 1.903 2
1.90
$ -round 1.903 0
2
Implementation:
LC_ALL=C builtin printf "%.*f\n" $_PRECISION $_VALUE
-simple-calc
Usage: -simple-calc [OPTION]... FORMULA
Performs simple floating point operations using awk, perl, python or bc - depending on which command is available.
Parameters:
FORMULA (required, pattern: "[+-]?[0-9]*\.?[0-9]+[/*^+-][0-9]*\.?[0-9]+")
The formula in the form of <NUM><OPERATOR><NUM>.
Options:
--round PRECISION (integer: 0-?)
Rounds the value with the given precision.
--using COMMAND (one of: [awk,bc,perl,python])
Specifies which command to use for calculation.
-----------------------------
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Examples:
$ -simple-calc 1.103+1.203
2.306
$ -simple-calc 1.103+1.203 --round 2
2.31
$ -simple-calc 1.109-0.8 --round 2
0.31
$ -simple-calc 1.109-0.8 --round 1
0.3
$ -simple-calc 1.109-0.8 --round 0
0
$ -simple-calc 2^1.2 --round 1
2.3
Implementation:
if [[ $_FORMULA =~ ^([+-]?[0-9]*\.?[0-9]+)([/*^+-])([0-9]*\.?[0-9]+)$ ]]; then
local leftNumber=${BASH_REMATCH[1]}
local operator=${BASH_REMATCH[2]}
local rightNumber=${BASH_REMATCH[3]}
else
echo "$_fn: Invalid formula."
return 1
fi
if [[ ! ${_using:-} ]]; then
if hash awk &>/dev/null; then
local _using=awk
elif hash perl &>/dev/null; then
local _using=perl
elif hash python &>/dev/null; then
local _using=python
elif hash bc &>/dev/null; then
local _using=bc
else
echo "$_fn: No supported command for floating operations available."
return 1
fi
fi
case $_using in
awk)
if [[ ${_round:-} ]]; then
LC_ALL=C awk "BEGIN{printf \"%.${_round}f\n\", $leftNumber$operator$rightNumber}"
else
LC_ALL=C awk "BEGIN{print $leftNumber$operator$rightNumber}"
fi
;;
bc)
if [[ ${_round:-} ]]; then
# https://stackoverflow.com/questions/16164925/using-fractional-exponent-with-bc
if [[ $operator == "^" && $rightNumber == *.* ]]; then
LC_ALL=C builtin printf "%.${_round}f\n" $(bc -l <<< "e($rightNumber*l($leftNumber))")
else
LC_ALL=C builtin printf "%.${_round}f\n" $(bc -l <<< "$leftNumber$operator$rightNumber")
fi
else
# https://stackoverflow.com/questions/16164925/using-fractional-exponent-with-bc
if [[ $operator == "^" && $rightNumber == *.* ]]; then
LC_ALL=C bc -l <<< "e($rightNumber*l($leftNumber))"
else
LC_ALL=C bc -l <<< "$leftNumber$operator$rightNumber"
fi
fi
;;
perl)
if [[ ${_round:-} ]]; then
if [[ $operator == "^" ]]; then
LC_ALL=C perl <<< "printf(\"%.${_round}f\", $leftNumber ** $rightNumber)"
else
LC_ALL=C perl <<< "printf(\"%.${_round}f\", $leftNumber $operator $rightNumber)"
fi
else
if [[ $operator == "^" ]]; then
LC_ALL=C perl <<< "print $leftNumber ** $rightNumber"
else
LC_ALL=C perl <<< "print $leftNumber $operator $rightNumber"
fi
fi
;;
python)
LC_ALL=C python -c "import math
if '$operator' == '^':
result=math.pow($leftNumber, $rightNumber)
else:
result=$leftNumber $operator $rightNumber
if ${_round:--1} > -1:
result=round(result, ${_round:-0})
result=str(result)
if result.endswith('.0'):
result=result[:-2]
print(result)
"
;;
esac
-test-all-math
Usage: -test-all-math [OPTION]...
Performs a selftest of all functions of this module by executing each function with option '--selftest'.
Options:
--help
Prints this help.
--tracecmd
Enables bash debug mode (set -x).
--selftest
Performs a self-test.
--
Terminates the option list.
Implementation:
-calc --selftest && echo || return 1
-round --selftest && echo || return 1
-simple-calc --selftest && echo || return 1