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 =.
Related
I am trying to write a basic code for (tower arithmetic), atleast thats what its called in my language.
I am new, so I need some help
Im gonna explain how it works:
you have to input 2 numbers number1, number2
those have to multiply result=(number1 * number2)
the result has to be multiplied with number2 again and +1 on number2 number1=(result * (number2+1))
this has to loop for 5 times
I hope you understand what I mean and someone can help me
#!/usr/bin/env bash
set -e # stops execution on first error
set -x # adds tracing of execution steps
read -p "Number 1: " number1
read -p "Number 2: " number2
result=$((number1 * number2))
for i in $(seq 1 5)
do
result=$((result * $((number2 + i))))
done
echo "Result: $result"
I'm new to Bash and I've been having issues with creating a script. What this script does is take numbers and add them to a total. However, I can't get total to work.It constantly claims that total is a non-variable despite it being assigned earlier in the program.
error message (8 is an example number being entered)
./adder: line 16: 0 = 0 + 8: attempted assignment to non-variable (error token is "= 0 + 8")
#!/bin/bash
clear
total=0
count=0
while [[ $choice != 0 ]]; do
echo Please enter a number or 0 to quit
read choice
if [[ $choice != 0 ]];
then
$(($total = $total + $choice))
$(($count = $count + 1))
echo Total is $total
echo
echo Total is derived from $count numbers
fi
done
exit 0
Get rid of some of the dollar signs in front of the variable names. They're optional inside of an arithmetic context, which is what ((...)) is. On the left-hand side of an assignment they're not just optional, they're forbidden, because = needs the variable name on the left rather than its value.
Also $((...)) should be plain ((...)) without the leading dollar sign. The dollar sign will capture the result of the expression and try to run it as a command. It'll try to run a command named 0 or 5 or whatever the computed value is.
You can write:
((total = $total + $choice))
((count = $count + 1))
or:
((total = total + choice))
((count = count + 1))
or even:
((total += choice))
((count += 1))
So I'm trying to create a random number limit the range from 1 to 10. There is a slightly different syntax between the two and I don't know if there any difference between those.
$(($RANDOM % 10 +1))
I tried this and it's working fine.
$(( ( RANDOM % 10 ) + 1 )). Including an extra () between RANDOM % 10 and +1 seems to work the same as the code above. But it has only one $ instead of 2.
Nothing. % has higher precedence (the same as *) than +, so the unparenthesized version is equivalent to the explicitly parenthesized one.
I originally missed that the second one also used RANDOM instead of $RANDOM. In an arithmetic context, a string is treated as an identifier, and is (recursively) expanded until you get an integer. If at any point the string is not a defined parameter, the value 0 is used instead. For example:
$ foo=bar
$ bar=6
$ echo $((bar))
6
$ echo $((foo))
6
In the last case, $foo expands to bar, which expands to 6.
IMO, it's better to use the explicit expansion. If the parameter isn't set due to a typo, you'll get an explicit error
$ foo=6
$ echo $((10 + $fo))
bash: 10 + : syntax error: operand expected (error token is "+ ")
rather than a silent "success" that might not be intended
$ echo $((10 + fo))
10 # ?? Why not 16?
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
}
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.