0

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" ```
3
  • 1
    There are two problems: too much backticks and repetitive code. Avoid backticks if the goal is to echo something, because echo does already the job! Plus, backticks cannot be nested. Use functions to factorize the code. Commented Apr 13, 2021 at 10:07
  • 1
    I think the problem is that your do statements aren't ended with done. Commented Apr 13, 2021 at 10:09
  • See eg linuxize.com/post/bash-for-loop Commented Apr 13, 2021 at 10:10

2 Answers 2

1

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.
Sign up to request clarification or add additional context in comments.

1 Comment

Wow so compact! I will not be using it in my report as I spent so long making my own one, but I very much appreciate your excellent answer and description
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.