This question already has answers here:
Calculating rounded percentage in Shell Script without using "bc"
(6 answers)
Closed 3 years ago.
I am trying to calculate on a Linux System.
I do have two different numbers, defined with a variable
$1= 1024
$2= 20
My task is now to calculate how many percent are 20 of 1024. The calculation would be (100/1024*20)
The problem is, that bash always says 0 with this type of code:
echo $((100/$1*$2))
Do anyone have an idea how i can calculate this?
you can do this using bc -l command.
Eg. echo "100/1024*20" | bc -l gives 1.953125
Your attempt didn't work because you are performing integer calculation:
100/1024 = 0 // integer calculation
100/1024 = 0.09765625 // floating point calculation
So, you need to explain in some way that floating point calculation is to be done.
You can do it as follows:
awk 'BEGIN {print (100/1024*20)}'
More examples can be found in this post.
You can tell bc to show results in 2 (or desired number of) decimal places.
Use below command
echo "scale=8; 100 / $1 * $2" | bc
On my computer, it reported something like below:
1.95312500
You can change the number of decimal places by passing correct numebr to 'scale' attribute.
Related
So I have two files in my directory that contain a number in each of them. I want to make a script that calculates the average of these two numbers. How would I write it? Would this be correct?
avg=$((${<file1.txt}-${<file2.txt})/2)
Your example does not work. Furthermore, your formula is probably incorrect. Here are two options without unnecessary cat:
avg=$(( (`<file1.txt` + `<file2.txt`) / 2 ))
or
avg=$(( ($(<file1.txt) + $(<file2.txt)) / 2 ))
I find the first one more readable though. Also be warned: this trivial approach will cause problems when your files contain more than just the plain numbers.
EDIT:
I should have noted that the first syntactical/legacy option which uses the backticks (` `) is no longer recommended and should be avoided. You can read more about the WHY here. Thanks at mklement0 for the link!
EDIT2:
According to Eric, the values are floating point numbers. You can't do this directly in bash because only integer numbers are supported. You have to use a little helper:
avg=$(bc <<< "( $(<file1.txt) + $(<file2.txt) ) / 2")
or maybe easier to understand
avg=$(echo "( $(<file1.txt) + $(<file2.txt) ) / 2" | bc)
For those who might wonder what bc is (see man bc):
bc is a language that supports arbitrary precision numbers with
interactive execution of statements.
Here is another alternative since perl is usually installed by default:
avg=$(perl -e 'print( ($ARGV[0] + $ARGV[1]) / 2 )' -- $(<file1.txt) $(<file2.txt))
You'll want to use a command substitution:
avg=$(($(cat file1.txt)-$(cat file2.txt)/2))
However, Bash is a pretty bad language for doing maths (at least unless it's completely integer maths). You might want to look into bc or a "real" language like Python.
I use qalculate as my day-to-day calculator and it's great! It is easy enough to type in something like:
(1+10^(-17.2/20)) / (1-10^(-17.2/20))
and get the right answer:
1.320289
But trying to get bc to do this sort of calculation in a bash script is frustrating. Google search spits back many pages demonstrating the simplest kinds of simple math examples using bc, but I have yet to find any pages tackling how to perform more complex calculations. When I type the following at CL:
echo 'scale=50; (1+10^(-17.2/20)) / (1-10^(-17.2/20))' | bc -l
I get the following warning-errors:
Runtime warning (func=(main), adr=25): non-zero scale in exponent
Runtime warning (func=(main), adr=44): non-zero scale in exponent
Runtime error (func=(main), adr=46): Divide by zero
If I try something similar but a little simpler like:
echo '(1-10^(-17.2/20))' | bc -l
I do get an answer, buts it's wrong and comes complete with a warning.
Runtime warning (func=(main), adr=18): non-zero scale in exponent
0
What could bc be having trouble with here, or rather what am I not doing correctly to get bc to perform these calculations properly?
Unfortunately, bc doesn't support exponents such as -17.2/20. If you don't require 50 decimal places of precision, one option would be to use another tool such as awk:
$ awk 'BEGIN{print (1+10^(-17.2/20)) / (1-10^(-17.2/20))}'
1.32029
You can pass variables to awk from your script like this:
$ awk -va="-17.2" -vb="20" 'BEGIN{print (1+10^(a/b)) / (1-10^(a/b))}'
1.32029
from the bc man page:
expr ^ expr:
The result of the expression is the value of the first raised to
the second. The second expression must be an integer.
but since if x = a^b, then ln(x) = ln(a^b) = b(ln(a)), we can see that x = exp(b(ln(a))), so if you want to raise things to fractional b's you can use that.
Note: In bc the actual exp and ln functions are e and l.
I am currently using a bash script to pipe a few other codes together but am new and have been stuck on this for the last day or so. Basically I need to count the number of lines within a file and then divide that by 4 to get the true number of objects in that file (each object takes up 4 lines).
For this I have looked around and ended up with the following code:
a=$(wc -l "${o}"*)
k=$(wc -l Unmatched_forward.fq)
x=4
#declare -i $a
declare -i $k
stats1_2=$((a / x))
stats2_2=$((k / x))
echo "${stats1_2} reads were joined."
echo "${stats2_2} reads were not joined."
Within this code ${o} is the output from a previous file however needs to have ".fq" added to the end but whenever I try to add that to the end it comes up the error message below I have been trying to use the "*" to run on the file of which there are no other files similar.
"Unmatched_forward.fq" is another output file which I want to count the number of objects in.
I am using the declare option because I read that otherwise the number will be in string form instead of an integer and so maths cannot be done.
If anyone can help and explain whats wrong that would be great.
The error message is:
Overlay_code.sh: line 638: declare: `1265272': not a valid identifier
Overlay_code.sh: line 638: declare: `Unmatched_forward.fq': not a valid identifier
Overlay_code.sh: line 643: 1265272 Unmatched_forward.fq: syntax error: invalid arithmetic operator (error token is ".fq")
Whats more confusing is I am suddenly getting the '1265272' number appearing and have no idea why!
You should check that your invocation of wc truly returns only an integer, because I think it is not. Probably the following happens
$> wc -l Unmatched_forward.fq
128 Unmatched_forward.fq
So it returns the line count and the filename.
The following should work
k=$(wc -l Unmatched_forward.fq | awk '{print $1}')
x=4
stats1_2=$((k / x))
Note that bash's (()) only supports integer math, so all results will get rounded. If you need floating point precision, check out bc
You mean declare -i k. When you include the $, you're causing the variable name to be replaced with its value. But you want to say that the variable k is integer-typed.
I am a beginner to shell scripting, so to get used to them I am starting off easy scripts. Trying to calculate the rate of interest for a "principal" amount, I wrote the below shell script.
But I am getting the output as:(150000*0.8)/100. I thought I will be getting mathematically solved output which is 1200. (pr=($principal*$rof)/100)
Can anyone help me in this? What mistake I have made?
principal=150000
rof=0.8
pr=($principal*$rof)/100
echo $pr
There are a couple of issues with this piece of code. Assuming you are using bash, the correct way is shown below,
Arithmetic operations are performed with the syntax,
x=$(( a + b ))
So, for your case, it becomes,
pr=$((( principle * rof ) / 100))
It is not possible to perform floating point operations in bash. You can use the unix utility bc for such purposes. In your case,
pr=`bc <<< "( $principle * $rof ) / 100"`
So, your complete code now becomes,
#!/bin/bash
principle=150000
rof=0.8
pr=`bc <<< "( $principle * $rof ) / 100"`
echo $pr
Bash does not support floating point number aritmetic, e.g. see this post
$> principle=150000;rof=8;pr=`expr $principle \* $rof / 1000`;echo $pr
1200
I have been futzing about learning bash tonight and I have been trying to create a random number sequence that uses all the numbers of a range and uses each digit just once. So something like inputting the range of 1-5 will output something like 4-3-5-2-1 or 2-5-1-3-4 and so on. I am as stuck as can be on this one.
Thanks!
the following command is not specific to bash, but it worked
seq 1 5 | shuf
a more bash specific with substrings
x=12345
for((i=5;i>0;i--));do
((r=RANDOM%i+1))
echo ${x:r-1:1}
x=${x:0:r-1}${x:r}
done