I need to write a bash shell script that asks the user for their name and 4 test scores worth different percentages which then calculates their total grade and outputs their letter grade.
The values are as follows:
Assignments 30%
Midterm 30%
quiz 10%
Final 30%
I've tried multyplying my variables by (30/100) after theyve been read but I can not get bash to accept mupltiple lines of arithmetic. So far I'e only been able to add them all up and divide by 4. I'm lost at this point any help is appreciated
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=$(expr $s1 + $s2 + $s3 + $s4)
avg=$(expr $total / 4)
if [ $avg -ge 80 ]
then
echo "$name's grade is an A"
elif [ $avg -ge 70 ]
then
echo "$name's grade is a B"
elif [ $avg -ge 60 ]
then
echo "$name's grade is a C"
elif [ $avg -ge 50 ]
then
echo "$names's grade is a D"
else
echo "$name's grade is an F"
fi
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 (eg 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
), eg
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.