简体   繁体   中英

Calculating grades with different weighted percentages and outputting a final grade in bash

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM