I have this practice problem that is asking me to take my existing code and insert variables for letter grades into the existing 'IF' statements for use in a case construct that reads off a sentence based on the letter grade assigned. The problem I'm having is how to set the variable in the first place, ie. do I need to do a whole new IF/ECHO line for the variable? How do I even word it in the first place?
Would it be something like grade=A?
Here's what I have so far: (EDITED TO INCLUDE SUGGESTIONS)
#!/bin/bash
# Bash shell script to calculate student average
# Usage: ./grade1.sh
# Declare some integer type variables
declare -i test1
declare -i test2
declare -i test3
declare -i test4
declare -i lab
declare -i sum
echo
echo "=================="
echo "Grade Calculator "
echo "=================="
echo
read -p "Enter first name: " firstname
read -p "Enter last name: " lastname
echo
read -p "Enter test score 1: " test1
read -p "Enter test score 2: " test2
read -p "Enter test score 3: " test3
read -p "Enter test score 4: " test4
read -p "Enter lab score: " lab
sum=$test1+$test2+$test3+$test4+$lab
average=$((sum/5))
VAR1=A
VAR2=B
VAR3=C
VAR4=D
VAR5=F
if [ $average -ge 90 ]; then
echo "Course grade: $VAR1"
elif [ $average -ge 80 ]; then
echo "Course grade: $VAR2"
elif [ $average -ge 70 ]; then
echo "Course grade: $VAR3"
elif [ $average -ge 60 ]; then
echo "Course grade: $VAR4"
elif [ $average -le 60 ]; then
echo "Course grade: $VAR5"
fi
echo
echo "Grade results . . ."
echo "Student name: $firstname $lastname"
echo "Total points: $sum"
echo "Course average: $average"
echo
case $grade in
A) echo "An 'A' represents superior course work."
;;
B) echo "A 'B' represents above average course work."
;;
C) echo "A 'C' represents average course work."
;;
D) echo "A 'D' represents below average course work."
;;
F) echo "An 'F' represents failing course work."
;;
esac
The task, as you stated it, doesn't make very much sense .... it would make sense if we have the number of grades and the shresholds completely variable. But, well, it's a practice problem, so they might require you doing some nonsense, just for the exercise.
As it is an exercise, I'll give you some pointers, but don't write down the whole solution.
Assuming that you are really supposed to do what you are asking here (and did not misunderstood the task), you are supposed to replace the literal grades (A, B, C, D, F) by variables. Since you have 5 grades, you need either 5 variables or an array of 5 elements. You asked for variables, so for this exercise, this is the way to go.
Since you have 5 variables, you need to invent 5 different names, for example
this=A
is=B
a=C
silly=D
exercise=F # Note: grade E does not exist
You can write these definition somewhere before they are first used, and feel free to use variable names which suit you better. Now bash knows about these variables, you can use them, for instance:
elif [ $average -ge 60 ]; then
echo "Course grade: $silly"
Now to two things you didn't ask for, but might be interested to know:
First, the calculation of average in your code is incorrect. If you set all tests and the lab to 1, you will get an average of 5 (try it out).
Second, in your case statement, you are using a variable grade, which you don't set anywhere. For example, at the place that you found out that the grade is D (and you did find this out, because you do an echo of this fact), you should set the variable
grade=$silly
This is a circumstance where you can let the shell (bash specifically) help you with your problem by utilizing arrays to help you build your text output and let character classes help with matches in your case statement.
For example, since you know you are grading on the traditional A-F 90-50 breakpoints, you can create several arrays to allow you index all associated information, e.g.
ltrgrades=( A B C D F )
numgrades=( 90 80 70 60 50 )
prefixes=( An A A A An )
comments=( "superior"
"above average"
"average"
"below average"
"failing" )
A function can handle all your output needs per-student by passing the associated index as its first argument:
results() {
echo
echo "Grade results . . ."
echo "Student name : $firstname $lastname"
echo "Total points : $sum"
echo "Course average: $average"
echo
echo "Course grade : ${ltrgrades[$1]}"
echo
echo "${prefixes[$1]} '${ltrgrades[$1]}' represents ${comments[$1]} course work."
}
Utilizing a heredoc simplifies providing multi-line output:
## use a heredoc for multi-line text
cat << EOF
"=================="
"Grade Calculator "
"=================="
EOF
(note: you could utilize a heredoc within the results function)
Finally, you can use character classes as your case matches and '*' to mark the default case, e.g.
case "${average%.*}" in
1?? ) results 0;;
9[0-9] ) results 0;;
8[0-9] ) results 1;;
7[0-9] ) results 2;;
6[0-9] ) results 3;;
* ) results 4;;
esac
Putting it altogether, you could do:
#!/bin/bash
# Bash shell script to calculate student average
# Usage: ./grade1.sh
ltrgrades=( A B C D F )
numgrades=( 90 80 70 60 50 )
prefixes=( An A A A An )
comments=( "superior"
"above average"
"average"
"below average"
"failing" )
results() {
echo
echo "Grade results . . ."
echo "Student name : $firstname $lastname"
echo "Total points : $sum"
echo "Course average: $average"
echo
echo "Course grade : ${ltrgrades[$1]}"
echo
echo "${prefixes[$1]} '${ltrgrades[$1]}' represents ${comments[$1]} course work."
}
## use a heredoc for multi-line text
cat << EOF
"=================="
"Grade Calculator "
"=================="
EOF
read -p "Enter first name : " firstname
read -p "Enter last name : " lastname
echo
read -p "Enter test score 1: " test1
read -p "Enter test score 2: " test2
read -p "Enter test score 3: " test3
read -p "Enter test score 4: " test4
read -p "Enter lab score : " lab
sum=$((test1 + test2 + test3 + test4 + lab))
average=$(echo "scale=2; $sum / 5" | bc)
case "${average%.*}" in
1?? ) results 0;;
9[0-9] ) results 0;;
8[0-9] ) results 1;;
7[0-9] ) results 2;;
6[0-9] ) results 3;;
* ) results 4;;
esac
Example Use/Output
$ bash grades.sh
"=================="
"Grade Calculator "
"=================="
Enter first name : John
Enter last name : Doe
Enter test score 1: 85
Enter test score 2: 93
Enter test score 3: 94
Enter test score 4: 91
Enter lab score : 92
Grade results . . .
Student name : John Doe
Total points : 455
Course average: 91.00
Course grade : A
An 'A' represents superior course work.
$ bash grades.sh
"=================="
"Grade Calculator "
"=================="
Enter first name : Mary
Enter last name : Jane
Enter test score 1: 86
Enter test score 2: 93
Enter test score 3: 72
Enter test score 4: 71
Enter lab score : 77
Grade results . . .
Student name : Mary Jane
Total points : 399
Course average: 79.80
Course grade : C
A 'C' represents average course work.
$ bash grades.sh
"=================="
"Grade Calculator "
"=================="
Enter first name : Sally
Enter last name : Smith
Enter test score 1: 55
Enter test score 2: 61
Enter test score 3: 42
Enter test score 4: 58
Enter lab score : 59
Grade results . . .
Student name : Sally Smith
Total points : 275
Course average: 55.00
Course grade : F
An 'F' represents failing course work.
Since the grades are computed as a floating-point value using bc, you can, and probably should, handle rounding (e.g a 79.5 rounds to 80 while a 79.4 remains 79. You can handle that with another variable score with something similar to:
sum=$((test1 + test2 + test3 + test4 + lab))
average=$(echo "scale=2; $sum / 5" | bc)
fract=${average#*.}
score=${average%.*}
(( ${fract:0:1} >= '5')) && ((score++))
case $score in
1?? ) results 0;;
9[0-9] ) results 0;;
8[0-9] ) results 1;;
7[0-9] ) results 2;;
6[0-9] ) results 3;;
* ) results 4;;
esac
Now Mary Jane's grade with an average of 79.80 is rounded to 80 a B instead of a C. It's up to you to determine how rounding is handled, this is just one way to approach it. You could re-write results to show both the computed average and rounded score, e.g.
results() {
cat << EOF
Grade results . . .
Student name : $firstname $lastname
Total points : $sum
Course average: $average ($score)
Course grade : ${ltrgrades[$1]}
${prefixes[$1]} '${ltrgrades[$1]}' represents ${comments[$1]} course work.
EOF
}
Related
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 (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.
I'm making a bash script in Vim editor for my operating systems fundamentals class, and I am having an extremely simple yet frustrating error occur where I cannot add variables together and set the sum to another variable. I've tried numerous formats to get this done, however it either prints out each value or a ": not found" error. Here is the code I have so far, I simply want to set the sum of the values for each test into the variable 'finalgrade' and print the output.
echo "Enter assignment mark (0 to 40): " ; read assignment
echo "Enter test1 mark (0 to 15): " ; read test1
echo "Enter test2 mark (0 to 15): " ; read test2
echo "Enter final exam mark (0 to 30): " ; read exam
finalgrade = $assignment + $test1 + $test2 + $exam
echo "Your final grade is : "$finalgrade
This is an example of what I get when I run it:
$ sh myscript
Enter assignment mark (0 to 40):
1
Enter test1 mark (0 to 15):
2
Enter test2 mark (0 to 15):
3
Enter final exam mark (0 to 30):
4
myscript: 5: myscript: finalgrade: not found
Your final grade is :
I instead expected the last line to be:
Your final grade is : 10
Thanks,
This line
finalgrade = $assignment + $test1 + $test2 + $exam
will not perform any math. Googling "bash math" will provide various ways to do this but here is one;
finalgrade=$((assignment + test1 + test2 + exam))
It is worth noting that your actual problem is that you have spaces beside the assignment = which causes bash to interpret this as a command "finalgrade" (not found) instead of an assignment. Variable assignments must not have spaces beside the =.
I am new to bash scripting and I am practicing some code.
I am trying to create a script that displays the following output and loops until the user types x or X
Menu 1
C) Calculation
X) Exit
C
Menu 2
Enter an integer or press X to exit:
22
Menu 3
+) Add
-) Subtract
+
Menu 2
Enter an integer or press X to exit:
33
The sum of 22 and 33 is 55
Menu 1
C) Calculation
X) Exit
c
Menu 2
Enter an integer or press X to exit:
50
Menu 3
+) Add
-) Subtract
-
Menu 2
Enter an integer or press X to exit:
23
The difference of 50 and 23 is 27
Menu 1
C) Calculation
X) Exit
X
Here is my code for it:
add ()
{
((sum=n1 + n2))
echo "The sum of $n1 and $n2 is $sum"
exit
}
subtract ()
{
((difference=n1 - n2))
echo "The difference of $n1 and $n2 is $difference"
exit
}
while true
do
echo "Menu 1"
echo "C) Calculation"
echo "X) Exit"
read opr
if [ ${opr} = 'x' ] || [ ${opr} = 'X' ]
then
break
elif [ ${opr} = 'c' ] || [ ${opr} = 'C' ]
then
echo "Menu 2"
echo "Enter an integer or press X to exit:"
fi
read n1
if [ $n1 = 'x' ] || [ $n1 = 'X' ]
then
break
else
echo "Menu3"
echo "+) Add"
echo "-) Subtract"
fi
read opr
if [ $opr = '+' ]
then
echo "Please enter another integer to perform addition"
read n2
add
elif [ $opr = '-' ]
echo "Please enter another integer to perform subtraction"
read n2
subtract
fi
done
I am receiving this error message:
./myscript.sh: line 72: syntax error near unexpected token fi'
./myscript.sh: line 72:fi'
I believe if I make menu1, menu2, and menu3 into functions I could achieve what I desire my output to be instead of this version of it.
But I know that I will still have a problem with those fi ... any idea where I should put them or what do I need to do for my code to work and not give an error?
Thanks
Pill
You simply forgot an then after elif
Look the general example from http://www.thegeekstuff.com/2010/06/bash-if-statement-examples/
If [ conditional expression1 ]
then
statement1
statement2
.
elif [ conditional expression2 ]
then
statement3
statement4
.
.
.
else
statement5
fi
In your code:
...
elif [ $opr = '-' ]
then <<<<<<< missing in your code!
echo "Please enter another integer to perform subtraction"
read n2
subtract
fi
EDIT: Why the program did not loop
If you write exit in your add and subtract method, your script will exit. Why it should not? Tip: remove both exit and the loop have a chance :-)
I am trying to calculate 20% of a where a is input by the user.
echo "Please enter your basic salary"
read a
#HRA
b=`expr (20 / 100)\* $a)`|bc
echo HRA is:$b
What's wrong in this expression, which is generating an error message?
The first problem is that you want to just pass the expression into bc for evaluation, so use echo rather than expr.
Secondly, the order of evaluation you have is going to calculate 20/100 as an integer first, which will be 0, then multiple the salary by that, resulting in 0. Reordering the calculation will resolve that.
So the following should work:
echo "Please enter your basic salary"
read a
#HRA
b=`echo $a " * 20 / 100"|bc`
echo HRA is:$b
Alternatively, if you prefer to use expr rather than bc, you can escape the problem characters to make this work:
echo "Please enter your basic salary"
read a
#HRA
b=`expr $a \* 20 \/ 100`
echo HRA is:$b
You can use let operator also in bash
#!/bin/bash
echo "Please enter your basic salary"
read a
let b=$a*20/100
echo HRA is:$b
I am trying to make a switch statement to work in tcsh but I am not sure why it is not working. I am displaying a menu on the screen and if the option is selected it shows the price and then goes back to the top and repeats until the exit option is selected.
#!/bin/csh
clear
echo -n "Petes A Pizza "
echo -n " Menu "
echo -n " "
echo -n " Make a selection "
echo -n " "
echo -n " A. Speciality Pizza "
echo -n " B. Veggi Lovers Pizza "
echo -n " C. Meat Lovers Pizza "
echo -n " D. Hawaiian Pizza "
echo -n " E. Cheese Pizza "
echo -n " F. Exit "
set a = $<
switch ($a)
case [A] :
set A = ((7.99 + 0.07))
echo $A
sleep 5
goto top
case [B] : #they choose option 2
set B = ((8.99 * 0.07) + 8.99)
echo $B
sleep 5
goto top
case [C] : #they choose option 3
set C = ((6.99 * 0.07) + 6.99)
echo $C
sleep 5
goto top
case [D] : #they choose option 4
set D = ((8.49 * 0.07) + 8.49)
echo $D
sleep 5
goto top
case [E] : #they choose option 5
set E = ((3.99 * 0.07) + 3.99)
echo $E
sleep 5
case [F] :
exit 0
breaksw
endsw
end
Here are a few suggestions that should be enough to help you get it working.
Change #!/bin/csh to #!bin/csh -f. This tells the shell not to read your ~/.cshrc file, which saves time and can avoid confusion. (If you accidentally write code that depends on aliases you've defined in your .cshrc, for example, your script won't work for anyone else.)
If you must clear the screen, the clear command is the way to do it -- but why? If I want to clear my screen before running your script, I'll do it myself, thank you very much. If I have information on my screen that I don't want to lose, I'll be annoyed when your script decides to erase it for me.
Change all the echo -ns to just echo. The -n option tells echo to print its output without a trailing newline; your entire menu will be printed on one line.
The square brackets in your case labels are unnecessary. case A : means the same thing as case [A] :. Note that you're requiring the user to provide input in upper case, which may be inconvenient.
set A = ((7.99 + 0.07))
...
set B = ((8.99 * 0.07) + 8.99)
These are inconsistent. It looks like you're trying to compute a base price plus 7% sales tax. For case B, a simpler expression for that is 8.99 * 1.07.
csh doesn't recognize this (( ... )) syntax; I wonder where you got the idea that it does. csh can do arithmetic using the # command:
# x = 2 + 2
# x ++
but it only operates on integers. The bc command can do floating-point calculations. You could write something like:
set B = `echo 'scale=5; 1.07 * 8.99' | bc`
Or, more simply:
set B = `echo '1.07 * 8.99' | bc -l
but bc -l may give you more digits than you want. man bc for more information on the bc command, its syntax, and how it works. Remember that the values of csh variables are strings, not numbers.
(I'm not sure bc is the best tool for this job.)
Finally, csh is not the best language for writing scripts. I've been using it for more years than I care to admit, and I sometimes have to resort to trial and error to find out how a given piece of syntax will behave; the syntax is poorly defined in many cases, and the man page doesn't always clear things up.
Suggested reading: "Csh Programming Considered Harmful", by Tom Christiansen.