Calculating rounded percentage in Shell Script without using bc
Use AWK (no bash-isms):
item=30
total=70
percent=$(awk "BEGIN { pc=100*${item}/${total}; i=int(pc); print (pc-i<0.5)?i:i+1 }")
echo $percent
43
Bash - Calculate Percentage
you can do this using bc -l
command.
Eg. echo "100/1024*20" | bc -l
gives 1.953125
How to calculate percentage in shell script
Posix-compliant solution using bc
:
#!/bin/sh
Percent="$(echo "
scale=2;
a = $DP * 100 / $SDC;
if (a > -1 && a < 0) { print "'"-0"'"; a*=-1; }
else if (a < 1 && a > 0) print 0;
a" | bc)"
That's kind of ugly with all of those special checks for cases when the answer is between -1 and 1 (exclusive) but not zero.
Let's use this Posix-compliant solution using awk
instead:
#!/bin/sh
Percent="$(echo "$DP" "$SDC" |awk '{printf "%.2f", $1 * 100 / $2}')"
Z shell can do this natively:
#!/bin/zsh
Percent="$(printf %.2f $(( DP * 100. / SDC )) )"
(The dot is necessary to instruct zsh to use floating point math.)
Native Posix solution using string manipulation (assumes integer inputs):
#!/bin/sh
# # e.g. round down e.g. round up
# # DP=1 SDC=3 DP=2 SDC=3
Percent=$(( DP * 100000 / SDC + 5)) # Percent=33338 Percent=66671
Whole=${Percent%???} # Whole=33 Whole=66
Percent=${Percent#$Whole} # Percent=338 Percent=671
Percent=$Whole.${Percent%?} # Percent=33.33 Percent=66.67
This calculates 1,000 times the desired answer so that we have all the data we need for the final resolution to the hundredths. It adds five so we can properly truncate the thousandths digit. We then define temporary variable $Whole
to be just the truncated integer value, we temporarily strip that from $Percent
, then we append a dot and the decimals, excluding the thousandths (which we made wrong so we could get the hundredths rounded properly).
BASH: Percentage change - how to calculate it? How to get absolute value without bc?
As you have mentioned that it's not necessary to do this in bash, I would recommend using awk:
awk -v t1="$time1" -v t2="$time2" 'BEGIN{print (t2-t1)/t1 * 100}'
Normally, awk is designed to process files but you can use the BEGIN
block to perform calculations without needing to pass any files to it. Shell variables can be passed to it using the -v
switch.
If you would like the result to be rounded, you can always use printf
:
awk -v t1="$time1" -v t2="$time2" 'BEGIN{printf "%.0f", (t2-t1)/t1 * 100}'
The %.0f
format specifier causes the result to be rounded to an integer (floating point with 0 decimal places).
Bash - echo percentage with no decimal point with result returned from bc command
You can try this approach:
echo "Compression ratio: $(bc <<< "scale=2; x = 8 / 7 * 100; scale = 0; x / 1")%";
Output:
Compression ratio: 114%
Calculating grades with different weighted percentages and outputting a final grade in bash
There are several things you can do following on from my comment above. First use the POSIX arithmetic operators (( ... ))
instead of the ancient expr
. Next, since bash only provides integer math, in order to work with your percentages, you must multiply the percentages by 100
and then divide by 100
to obtain the total
. You don't divide by 4
, your percentages already account for weighting scores to arrive at 100%. Further, in bash and then you must rely on a floating point utility like bc
to handle all floating point calculations, including the division to arrive at your avg
.
However, that alone will not remove the rounding error when you go to compare avg
in your if then elif ... else
statements (e.g. treating 79.9
as 79
). Using bc
to obtain 79.9
you still need a way to properly handle rounding to obtain 80
from 79.5
(or better) and 79
for anything less than 79.5
but greater than or equal to 78.5
.
Thankfully printf -v
provides a convenient way to store the results of a conversion as a variable and handle the rounding since it uses the the C-print conversion logic. For example, to save the results of avg
converted by bc
and then properly rounded (saved in the variable name rounded
) you could do:
total=$((s1 * 30 + s2 * 30 + s3 * 10 + s4 * 30))
avg=$((total / 100)) ## not used but saved for output demonstration below
printf -v rounded "%.0f" $(printf "scale=2; $total/100\n" | bc)
Putting it altogether you could do:
#!/bin/bash
echo "What is your name?"
read name
echo "What is your score on the Assignment?"
read s1
echo "What is your score on the Quiz?"
read s2
echo "What is your score on the Midterm Exam?"
read s3
echo "What is your score on the Final Exam?"
read s4
total=$((s1 * 30 + s2 * 30 + s3 * 10 + s4 * 30))
avg=$((total / 100))
printf -v rounded "%.0f" $(printf "scale=2; $total/100\n" | bc)
if [ "$rounded" -ge 80 ]
then
echo "$name's grade is an A"
elif [ "$rounded" -ge 70 ]
then
echo "$name's grade is a B"
elif [ "$rounded" -ge 60 ]
then
echo "$name's grade is a C"
elif [ "$rounded" -ge 50 ]
then
echo "$names's grade is a D"
else
echo "$name's grade is an F"
fi
To confirm the rounding his handled properly, you can add a simply printf
following saving rounded
, for example add:
printf "\ntotal: %d\navg : %d\nrounded avg: %d\n\n" \
"$total" "$avg" "$rounded"
Then to confirm, enter scores that would result in a number that if not rounded properly would result in a "B"
instead of an "A"
(which you liberally give at a score of 80
), e.g.
Example Use/Output
$ bash student.sh
What is your name?
John
What is your score on the Assignment?
78
What is your score on the Quiz?
80
What is your score on the Midterm Exam?
81
What is your score on the Final Exam?
81
total: 7980
avg : 79
rounded avg: 80
John's grade is an A
Using a case
Statement
You may also want to consider eliminating your long chain of if then elif then elif then ...
statement with a simple case ... esac
statement that will greatly clean things up. For example you can replace the entire collection of if then elif ... else
with
printf "%s's grade is an '" "$name"
case "$rounded" in
100 | [89]? ) echo "A'";;
7? ) echo "B'";;
6? ) echo "C'";;
5? ) echo "D'";;
* ) echo "F'";;
esac
Example Use/Output With case
$ bash student.sh
What is your name?
John
What is your score on the Assignment?
78
What is your score on the Quiz?
80
What is your score on the Midterm Exam?
80
What is your score on the Final Exam?
80
total: 7940
avg : 79
rounded avg: 79
John's grade is an 'B'
Look things over and let me know if you have questions.
division math in a bash script without bc?
use bash's math builtin. it just prints out the integers/whole numbers. so if you want to discard/truncate the decimal places use something like:
echo $(((11*100)/531)) # prints 2
Shell Percentage Calculation With Very Large Numbers
You can use printf
to fix your number:
RESULT=$(printf "%.0f" $(echo "scale=8;100/$MAX*$RESULT" | bc))
How to calculate half of total memory rounded up to next 1G boundary?
Assuming everything in G, you need:
free -h | awk 'NR==2{print(int(substr($2,1,length($2)-1)/2+.5))}'
Explanation:
To print the second line, you need
free -h | awk 'NR==2{print}'
To print the second field of the second line, you need
free -h | awk 'NR==2{print($2)}' # prints 7.7G
To remove the final G, you need substring which start at 1st character and contains as many character as in the string, minus 1.
free -h | awk 'NR==2{print(substr($2,1,length($2)-1))}' # prints 7.7
To get half of it, you need
free -h | awk 'NR==2{print(substr($2,1,length($2)-1)/2)}' # prints 3.85
To round to the closest integer, you need to add 0.5 and truncate the fractional part (which is done by int
function)
free -h | awk 'NR==2{print(int(substr($2,1,length($2)-1)/2+.5))}' # prints 4
Related Topics
Iterating Over Lists in Makefiles
Configure and Build Opencv to Custom Ffmpeg Install
"Current" in Linux Kernel Code
How to Force a Firefox Page Refresh from Linux Console
How to Tail -F the Latest Log File with a Given Pattern
How to Implement a Practical Fiber Scheduler
Custom Git Command Autocompletion
How to Make Sure the Floating Point Arithmetic Result the Same in Both Linux and Windows
Changing the PHPmyadmin Default Url
Cannot Sudo Su Anymore, "No Tty Present and No Askpass Program Specified"
Simulate Network Latency on Specific Port Using Tc
Retrieve Plain Text Script from Compiled Bash Script
Need a Simple Linux C++ Ide (Android Ndk)
Error Running Make: Missing Separator (Did You Mean Tab Instead of 8 Spaces)