Reputation: 65
I'm learning how to write scripts, and am having an issue with an if statement. The script reads in user input for their scores, and calculates their average grade. However, if the user enters a score lower than 0 or greater than 100, the script should ask them to re-enter a valid value. Here is the code below:
#!/bin/bash
SCORE=0
AVERAGE=0
SUM=0
NUM=0
while true
do
# Read score from user
echo -n "Enter your score [0-100] ('q' for quit): "; read SCORE
# Validate user input
if (($SCORE < 0)) || (($SCORE > 100))
then
echo "Invalid input, try again: "
#elif (( $SCORE == "q" ))
elif [ "$SCORE" == "q" ]
then
echo "Average rating: $AVERAGE%."
break
else
# Perform calculation
SUM=$[$SUM + $SCORE]
NUM=$[$NUM +1]
AVERAGE=$[$SUM / $NUM]
echo "Average rating: $AVERAGE%"
fi
done
Notice I have the elif
commented out. When I write that elif with []
notation, it works as expected. However, if I switch to the elif which uses (())
, and enter "00" as my score, the program executes the "then" block on and exits. Why is it doing that? 00 is not 'q'.
Upvotes: 1
Views: 224
Reputation: 85865
The line (( $SCORE == "q" ))
does not work for the exact reason you are suggesting. The ((..))
is only meant for arithmetic operations and string operations cannot be done within it. You need to use the test
operator [
or [[
for it.
Quoting from the GNU bash
documentation
6.5 Shell Arithmetic
The shell allows arithmetic expressions to be evaluated, as one of the shell expansions or by using the
((
compound command, thelet
builtin, or the-i
option to thedeclare
builtin.Evaluation is done in fixed-width integers with no check for overflow, though division by 0 is trapped and flagged as an error. The operators and their precedence, associativity, and values are the same as in the
C
language.
Some of the standard expression involving strings when used with the classic test
operator ([
)
Operator Syntax-Description
-------------------------------------------------------------------------------------------
-z <STRING> True, if <STRING> is empty.
-n <STRING> True, if <STRING> is not empty (this is the default operation).
<STRING1> = <STRING2> True, if the strings are equal.
<STRING1> != <STRING2> True, if the strings are not equal.
The one you have used [ "$SCORE" == "q" ]
is synonymous to using [ "$SCORE" = "q" ]
in a way only literal string match is performed but not POSIX
compatible as the latter.
The ==
comparison operator behaves differently within a double-brackets than within single brackets (both literal & glob matching)
[[ $a == z* ]] # True if $a starts with an "z" (pattern matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).
Also you could re-write your statements entirely in arithmetic context using the ((..))
operator over the legacy $[ ... ]
operator.
sum=$((sum + score))
((num++))
(( num != 0 )) && average=$(( sum / num ))
printf "%0.2f\n" $num
Note that I have lowercased the variable names to distinguish them from the environment variables (shell system variables) but just as user define variables. And used printf
over echo
to introduce POSIX
guaranteed floating point formatting options which echo
does not allow.
Upvotes: 5