gnarm
gnarm

Reputation: 59

Nested if statements with nested loops

I am trying to make a simple calculator. I am sure if you even just glanced at this code you will see what I am trying to do. Enter a number, then choose an operand, then vim should print out a table up to 15 with your number and operand...

Maybe this method is silly, trying to nest a load of loops in nested if statements. But I am new to bash.

The error is 'Unexpected token near else' line 24 but I feel there is a fundamental issue with the nests I do not understand.

Here is current code.

#!/bin/bash
choice=6
read -p "Enter a number bruv" num
#choose operand.
echo "Now choose an operand comrade"
#choices

echo "1. *"
echo "2. +"
echo "3. -"
echo "4. /"
echo "5. ^"

echo -n "Please choose [1,2,3,4,5]"

while [ $choice -eq 6 ]; do
        read choice

        if [ $choice -eq 1 ] ; then
                for((i=0;i<=15;i++))
                do
                echo -n "$i * $num = $[ $i * $num ] "
                echo " "
        else
                if [ $choice -eq 2 ] ; then
                        for((i=0;i<=15;i++))
                        do
                        echo -n "$i + $num = $[ $i + $num ] "
                        echo " "
                else
                        if [ $choice -eq 3 ] ; then
                                for((i=0;i<=15;i++))
                                do
                                echo -n "$i - $num = $[ $i - $num ] "
                                echo " "
                        else
                                if [ $choice -eq 4 ] ; then
                                        for((i=0;i<=15;i++))
                                        do
                                        echo -n "$i / $num = $[ $i / $num ] "
                                        echo " "
                                else
                                        if [ $choice -eq 5 ] ; then
                                       for((i=0;i<=15;i++))
                                        do
                                        echo -n "$i ^$num = $[ $i ^$num ] "
                                        echo " "
                                else echo "Please choose between 1 and 5!!!"
                                          echo "1. *"
                                         echo "2. +"
                                          echo "3. -"
                                          echo "4. /"
                                           echo "5. ^"
                                        echo -n "Please choose [1,2,3,4,5]"
                              fi
                     fi
             fi
        fi
     fi
done


                                                                       

Would it be better to implement this?

# !/bin/bash
  
# Take user Input
echo "Enter number : "
read a

  
# Input type of operation
echo "Enter Choice :"
echo "1. Addition"
echo "2. Subtraction"
echo "3. Multiplication"
echo "4. Division"
echo "5. Power"
read ch
  
# Switch Case to perform
# calulator operations
case $ch in
  1)res=`for((i=0;i<=15;i++))
                                do
                                echo -n "$i - $num = $[ $i - $num ] "
                                echo " "`
  ;;
  2)res=`for((i=0;i<=15;i++))
                                do
                                echo -n "$i - $num = $[ $i - $num ] "
                                echo " "`
  ;;
  3)res=`for((i=0;i<=15;i++))
                                do
                                echo -n "$i - $num = $[ $i - $num ] "
                                echo " "`
  ;;
  4)res=`for((i=0;i<=15;i++))
                                do
                                echo -n "$i - $num = $[ $i - $num ] "
                                echo " "c`
  ;;
esac
echo "Result : $res" ```

Upvotes: 0

Views: 326

Answers (2)

Paul Hodges
Paul Hodges

Reputation: 15273

Use https://www.shellcheck.net/

 
Line 20:
                for((i=0;i<=15;i++))
                ^-- SC1009: The mentioned syntax error was in this for loop.
                   ^-- SC1073: Couldn't parse this arithmetic for condition. Fix to allow more checks.
 
Line 21:
                do
                ^-- SC1061: Couldn't find 'done' for this 'do'.
 
Line 24:
        else
        ^-- SC1062: Expected 'done' matching previously mentioned 'do'.
            ^-- SC1072: Unexpected keyword/token. Fix any mentioned problems and try again.

An alternate take:

read -p "Enter a number and an operator: " -a a
for n in {1..15}; do printf "%+10s\n" $((${a[@]} $n)); done

-p tells read to supply a prompt. -a reads into an array (named a here).
{1..15} is built-in bash sequence syntax.
%+10s tells printf to space-pad/right justify out to 10 characters.
${a[@]} is replaced with all the elements of the array - the number and then operator.
$(( ... )) does arithmetic processing and replaces itself with the result (as a string).

So if you enter "10 *" then $((${a[@]} $n)) processes 10 * 1 the first time, so printf "%+10s\n" $((${a[@]} $n)) outputs " 10" with a trailing newline character. Then the loop replaces the 1 with the next number on each iteration, up to 15. This also allows other operators, such as % for modulus, but will crash if something invalid is given.

If you want error checking -

while [[ ! "${a[*]}" =~ ^[0-9][0-9]*\ ([*/+-]|\*\*)$ ]]
do read -p "Enter a, integer and an operator (one of: + - * / **) " -a a
done
for n in {1..15}; do printf "%+10s\n" $((${a[@]} $n)); done

If you want floating point math, try bc:

while [[ ! "${a[*]}" =~ ^[0-9]+.?[0-9]*\ ([*/+-]|\*\*)$ ]]
do read -p "Enter a, integer and an operator (one of: + - * / **) " -a a
done
for n in {1..15}; do printf "%+15s\n" $(bc -l  <<< "scale=3;${a[@]} $n"); done

Personally, I'd put the inputs on the command line. Less futzy, though it might require quoting the * operator, depending on how you run it and what's in the directory.

#!/bin/bash

me=${0##*/}
use="
  use: $me {number} {operator} [iterations] [scale]

  Numbers may include one decimal point.
  Operators must be one of: + - * / **

"
while getopts "n:o:i:s:" arg
do case $arg in
   n) n="$OPTARG" ;;
   o) o="$OPTARG" ;;
   i) i="$OPTARG" ;;
   s) s="$OPTARG" ;;
   *) printf "%s\n" "Invalid argument '$arg' $use";
      exit 1;;
   esac
done

[[ -n "$n" && -n "$o"             ]] || { echo                                                     "$use"; exit 1; }
[[ "$n"       =~ ^[0-9]+.?[0-9]*$ ]] || { printf "%s\n" "Invalid number   '$n'                      $use"; exit 1; }
[[ "$o"       =~ ^([*/^+-]|\*\*)$ ]] || { printf "%s\n" "Invalid operator '$o'                      $use"; exit 1; }
[[ "${i:=15}" =~ ^[0-9]+.?[0-9.]$ ]] || { printf "%s\n" "Invalid number   '$i'                      $use"; exit 1; }
[[ "${s:=3}"  =~ ^[0-9]+$         ]] || { printf "%s\n" "Invalid scale    '$s' (must be an integer) $use"; exit 1; }

c=1
while ((c < i))
do printf "%+15s\n" $(bc -l <<< "scale=$s; $n $o $c")
   ((c++))
done

Upvotes: 0

Edouard Thiel
Edouard Thiel

Reputation: 6218

Here is a solution, using a function:

#! /bin/bash

ITER_MAX=15

show_values() # n op
{
    local n=$1 op=$2
    for ((i=0; i<=ITER_MAX; i++)); do
        ((i>0)) && echo -n " ; "
        echo -n "$i $op $n = $((i $op n))"
    done
    echo
}

# Take user Input
read -p "Enter number : " a

# Input type of operation
echo "Enter Choice (Ctrl+C to stop):"
PS3=">> "
select ch in Addition Subtraction Multiplication Division Power ; do
  case "$ch" in
    Add*) op="+" ;;
    Sub*) op="-" ;;
    Mul*) op="*" ;;
    Div*) op="/" ;;
    Pow*) op="**" ;;
    *) echo "Bad choice, abort" >&2 ; break ;;
  esac
  show_values "$a" "$op"
done

Some explanations:

  • (( )) is arithmetic evaluation and $(( )) is arithmetic expansion
  • ((i>0)) && echo -n " ; " is equivalent to if ((i>0)); then echo -n " ; " ; fi
  • read -p "Enter number : " a is equivalent to echo -n "Enter number : " ; read a
  • about select, see help select in your bash terminal.

Upvotes: 1

Related Questions