Bash Script: Decimal increments in loop (cannot do) [duplicate] - linux

Here is my script:
d1=0.003
d2=0.0008
d1d2=$((d1 + d2))
mean1=7
mean2=5
meandiff=$((mean1 - mean2))
echo $meandiff
echo $d1d2
But instead of getting my intended output of:
0.0038
2
I am getting the error Invalid Arithmetic Operator, (error token is ".003")?

bash does not support floating-point arithmetic. You need to use an external utility like bc.
# Like everything else in shell, these are strings, not
# floating-point values
d1=0.003
d2=0.0008
# bc parses its input to perform math
d1d2=$(echo "$d1 + $d2" | bc)
# These, too, are strings (not integers)
mean1=7
mean2=5
# $((...)) is a built-in construct that can parse
# its contents as integers; valid identifiers
# are recursively resolved as variables.
meandiff=$((mean1 - mean2))

Another way to calculate floating numbers, is by using AWK rounding capability, for example:
a=502.709672592
b=501.627497268
echo "$a $b" | awk '{print $1 - $2}'
1.08218

In case you do not need floating point precision, you may simply strip off the decimal part.
echo $var | cut -d "." -f 1 | cut -d "," -f 1
cuts the integer part of the value. The reason to use cut twice is to parse integer part in case a regional setting may use dots to separate decimals and some others may use commas.
Edit:
Or, to automate the regional settings one may use locale.
echo $var | cut -d $(locale decimal_point) -f 1

You can change the shell which you are using. If you are executing your script with bash shell bash scriptname.sh try using ksh for your script execution. Bash doesn't support arithmetic operations that involve floating point numbers.

Big shout-out to the bc command - it totally saved my day! It's a simple answer, but it worked like a charm.
a=1.1
b=1.1
echo $a + $b | bc -l
# Output:
2.2
#SUM
sum=$(echo $a + $b | bc -l)
echo $sum
# Output
2.2
bc is a command-line calculator, which allows users to perform mathematical calculations on the terminal.

Related

eliminate subshells for faster process?

I've read that scripts that are calling for a subshell are slow, which would explain why my script are slow.
for example here, where I'm running a loop that gets an number from an array, is this running a subshell everytime, and can this be solved without using subshells?
mmode=1
modes[1,2]="9,12,18,19,20,30,43,44,45,46,47,48,49"
until [[ -z $kik ]];do
((++mloop))
kik=$(echo ${modes[$mmode,2]} | cut -d "," -f $mloop)
filename=$(basename "$f")
# is all these lines
xcolorall=$((xcolorall+$storednr)
# also triggering
pros2=$(echo "100/$totpix*$xcolorall" | bc -l)
IFS='.' read -r pros5 pros6 <<< "$pros2"
procenthittotal2=$pros5.${pros6:0:2}
#subshells and if,
# is it possible to circumvent it?
#and more of the same code..
done
updated:
the pros2 variable is calculating percent, how many % xcolorall are of totpix and the kik variable is getting a number from the array modes, informing the loop about what color it should count in this loop.
I suspect these are the main hoggers, is there anyway to do this without subshells?
You can replace all the subshells and extern commands shown in your question with bash built-ins.
kik=$(echo ${modes[$mmode,2]} | cut -d "," -f $mloop) can be replaced by
mapfile -d, -t -s$((mloop-1)) -n1 kik <<< "${modes[$mmode,2]}".
If $mmode is constant here, better replace the whole loop with
while IFS=, read -r kik; do ...; done <<< "${modes[$mmode,2]}".
filename=$(basename "$f") can be replaced by
filename=${f##*/} which runs 100 times faster, see benchmark.
pros2=$(echo "100/$totpix*$xcolorall" | bc -l) can be replaced by
(( pros2 = 100 * xcolorall / totpix )) if you don't care for the decimals, or by
precision=2; (( pros = 10**precision * 100 * xcolorall / totpix )); printf -v pros "%0${precision}d" "$pros"; pros="${pros:0: -precision}.${pros: -precision}" if you want 2 decimal places.
Of course you can leave out the last commands (for turning 12345 into 123.45) until you really need the decimal number.
But if speed really matters, write the script in another language. I think awk, perl, or python would be a good match here.

More convenient way to do arithmetic with program output at the shell?

I usually need to run programs to do some file checking, like say use wc to count the lines of a file and then do some arithmetic with it. Usually the way I do this is just getting the output and then doing the arithmetic by opening a python terminal or whichever software can be used to do so.
If I have to do it many times, then this gets a bit annoying, and I'd like to have some method to take the output directly and do the arithmetic that I want. For instance, one that I like is using perl in the following way, assuming I have to take the output of wc and divide it by 12:
perl -e 'print `wc -l file`/12'
This can be useful but gets annoying after a while. Since this is probably something people need to do all the time, I'd like to know what better faster methods people use to do this fast. I've seen expr might be even better, but I get a syntax error when passing it the output of something bound in ``, like above. So basically the shortest, most efficient way one can do this simple arithmetic in linux terminals from file outputs.
Double parentheses ((...)) perform arithmetic, and with a dollar sign $((...)) you can get the result as a string.
echo $((`wc -l < file` / 12))
echo $(($(wc -l < file) / 12))
You can use variables and they don't need dollar signs. Both var and $var are acceptable:
lines=$(wc -l < file)
echo $((lines / 12))
if ((lines * 42 + 17 > 630)); then
...
fi
So basically I have tested with a code on my bash:
Multiline code:
a=$(echo "hi" | wc -l)
echo $a
b=`expr $a + 2`
echo $b
Which I have changed to one line:
echo `expr $(echo "hi" | wc -l) + 20`
echo "hi" | wc -l is calculating no of lines and is within $() which makes it as one variable and evaluate its value
Then expr takes two arguements here and make sure you to use space before and after the operator and use a backtic(`) to evaluate thi and doing echo finally

passing bash expression to AWK for floating point arithmetic

I was trying to do floating point arithmetic in bash, but as floats are not supported, was trying to get the solution using AWK. Below is the issue i am facing:
I see this working fine for me:
code
echo - | awk '{printf("%04.4f \n", (-225.2*7+30*6)/17 + (19^2)/9) }'
output
-42.0301
But my motive is to "read an expression" and compute value correct to 4 decimals, so tried below code inputting same expression (-225.2*7+30*6)/17 + (19^2)/9) and its giving incorrect values(i guess variable is passed as string to awk):
code
read inpt
echo - | awk -v input=$inpt '{printf("%04.4f \n", input) }'
output
0.0000
Note: Please ignore the space around second + in this example expression, that i can remove using sed or similar methods(with space i get syntactical error in awk while passing variable from bash).
Any help is highly appreciated. Thanks in advance
PS: the bash version in my case is "bash-4.2". I guess its the version of bash preventing me using from many other options.
You can't evaluate data in a variable in awk out of the box. In this case you need to write an arithmetic evaluator or use a pre-existing one, like https://github.com/radare/radare2-bindings/blob/master/awk/calc.awk . Once you fix that missing parenthesis and quote your expression properly, you can:
$ echo "((-225.2*7+30*6)/17 + (19^2)/9)" | awk -f calc.awk
((-225.2*7+30*6)/17 + (19^2)/9) = -42.0301
I recommend and have upvoted James Brown's answer. But if you need something here and now with no external dependencies, you can simply interpolate the input into the script.
awk "END { printf("%04.4f\n", $input) }" </dev/null
This is functionally equivalent to using eval so if this isn't deployed where you know you can trust the input (e.g. because it comes from a controlled process, not an actual user) you will need to perform some sort of sanitization (and even then probably cope with odd or outright misleading error messages if the input isn't a well-formed Awk expression).
read -p "Input an Awk expression: " input
case $input in
*[!-+/*()^%0-9]*)
echo "$0: invalid input" >&2
exit 1;;
esac
awk ...
Notice also the construct to avoid the basically useless echo. Redirecting input from /dev/null and putting your code in the END (or BEGIN) block is a standard technique for running an arbitrary piece of Awk script without requiring any input.
First, I don't see anything wrong in the command, it should work. Please try again the exact commands posted and provide exact details if the issue persists.
However, if all you need is formating the output, you can do it directly with printf.
$ read input
12.34
$ printf '%4.4f' $input
12.3400
EDIT:
If you need to format the output after performing some calculation, then you can alternatively use bc. (awk should still work)
$ echo "scale=4; (-225.2*7+30*6)/17+(19^2)/9" | bc
-42.0300
You can use variables in the expression as usual,
$ read inpt
1234
$ echo "scale=4; $inpt * 0.01" | bc
12.34
Is this what you are looking for ?
echo "25 50"| awk '{print $2,"/",$1}' | bc

Perform arithmetic on a number in a pipe

Extremely similar to BASH: how to perform arithmetic on numbers in a pipe
However, that answer relates to several numbers and so uses loops, which seems silly as I only have 1 number
date +%s | echo $((1400000000 - $number)) | ...
Edit: I plan on passing this command to watch, so using shell expansion will not work correctly... I am aware I could just make it its own script, but I want to know if I can just one-line the whole thing
You can use bc to do arithmetic in the shell.
echo "1400000000 - `date +%s`" | bc
echo $((1400000000 - $(date +%s))) | ...
I can't do shell expansion, but instead managed to get a variant of John C's answer working:
date +%s | xargs -I d echo "1400000000 - d" | bc | ...
There are, of course, several other options

How do I divide in the Linux console?

I have to variables and I want to find the value of one divided by the other. What commands should I use to do this?
In the bash shell, surround arithmetic expressions with $(( ... ))
$ echo $(( 7 / 3 ))
2
Although I think you are limited to integers.
echo 5/2 | bc -l
2.50000000000000000000
this '-l' option in 'bc' allows floating results
Better way is to use "bc", an arbitrary precision calculator.
variable=$(echo "OPTIONS; OPERATIONS" | bc)
ex:
my_var=$(echo "scale=5; $temp_var/100 + $temp_var2" | bc)
where "scale=5" is accuracy.
man bc
comes with several usage examples.
You can use awk which is a utility/language designed for data extraction
e.g. for 1.2/3.4
>echo 1.2 3.4 | awk '{ print $2/$1 }'
0.352941
I still prefer using dc, which is an RPN calculator, so quick session to divide 67 by 18 with 4 digits precision would look like
>dc
4k
67
18/p
3.7222
q
>
Obviously, much more available: man dc
In bash, if you don't need decimals in your division, you can do:
>echo $((5+6))
11
>echo $((10/2))
5
>echo $((10/3))
3
I assume that by Linux console you mean Bash.
If X and Y are your variables, $(($X / $Y)) returns what you ask for.
Example of integer division using bash to divide $a by $b:
echo $((a/b))
Something else you could do using raytrace's answer. You could use the stdout of another shell call using backticks to then do some calculations. For instance I wanted to know the file size of the top 100 lines from a couple of files. The original size from wc -c is in bytes, I want to know kilobytes. Here's what I did:
echo `cat * | head -n 100 | wc -c` / 1024 | bc -l
You should try to use:
echo "scale=4;$variablename/3"|bc
you can also use perl -e
perl -e 'print 67/8'
I also had the same problem. It's easy to divide integer numbers but decimal numbers are not that easy.
if you have 2 numbers like 3.14 and 2.35 and divide the numbers then,
the code will be Division=echo 3.14 / 2.35 | bc
echo "$Division"
the quotes are different. Don't be confused, it's situated just under the esc button on your keyboard.
THE ONLY DIFFERENCE IS THE | bc and also here echo works as an operator for the arithmetic calculations in stead of printing.
So, I had added echo "$Division" for printing the value. Let me know if it works for you. Thank you.

Resources