Primary script:

#!/bin/bash

source utilities/menu.sh
source utilities/input_validation.sh

header="Choose from the options below(Example: 1, 2, 3 etc...):"
options=("1" "2" "3" "4" "5")
option_descriptions=("Add" "Subtract" "Multiply" "Divide" "Exit")
prompt="> "

calculate(){
  if [[ $option == "1" ]]
  then
    echo "$num1 + $num2" | bc
  elif [[ $option == "2" ]]
  then
    echo "$num1 - $num2" | bc
  elif [[ $option == "3" ]]
  then
    echo "$num1 * $num2" | bc
  elif [[ $option == "4" && $num2 != "0" ]]
  then
    echo "scale=2; $num1 / $num2" | bc
  elif [[ $option == "4" && $num2 == "0" ]]
  then
    echo "Dividing by 0 is undefined"
  else
    exit=0
  fi
}

calculator(){
  persistant_get_menu_option "$header" "$options" "$option_descriptions"
  option=$?
  if [[ $option == "5" ]]
  then
    return 1
  else
    persistent_get_number "$prompt"
    num1=$?
    persistent_get_number "$prompt"
    num2=$?
    echo "------------------------------------"
    calculate "$option" "$num1" "$num2"
    echo "------------------------------------"
    return 0
   fi
}

will_exit="0"
while [[ $will_exit == "0" ]]
do
  calculator
  if ! [ $? = 0 ]
  then
    will_exit="1"
  else
    will_exit="0"
  fi
done

echo "Good bye"

Utility scripts:

#!/bin/bash

is_option(){
  for option in "${options[@]}"
  do
      if [ "${option}" = "$choice" ]
      then
          return 0
      fi
  done
  return 1
}

persistant_get_menu_option(){
  choice=""
  will_exit="0"
  while [[ $will_exit == "0" ]]
  do
    echo ""
    echo "$header"
    echo "-----------------------------------------------------------"
    i=0
    for description in "${option_descriptions[@]}"
    do
      echo "${options[i]}: $description"
      i=$((i + 1))
    done
    echo ""
    read -r choice
    is_option "${options[@]}" "$choice"
    if [[ $? == "1" ]]
    then
      echo "You must choose from the options given"
    else
      will_exit="1"
    fi
  done
  return "$choice"
}

And:

#!/bin/bash

is_numerical(){
  if ! [[ $user_input =~ (^-?0\.[0-9]*[1-9]+[0-9]*$)|(^-?[1-9]+[0-9]*((\.[0-9]*[1-9]+[0-9]*$)|(\.[0-9]+)))|(^-?[1-9]+[0-9]*$)|(^0$){1} ]]
  then
    return 0
  else
    return 1
  fi
}

persistent_get_number(){
  user_input=""
  will_exit="0"
  while [ $will_exit == "0" ]
  do
    echo "$prompt"
    read -r user_input
    is_numerical "$user_input"
    if ! [ $? = 0 ]
    then
      echo "Input must be numerical"
    else
      will_exit="1"
    fi
  done
  return "$user_input"
}

When I try to add 1.2 and 1.2 I get this(the result is 4):

1
>
1.2
utilities/input_validation.sh: line 27: return: 1.2: numeric argument required
>
1.2
utilities/input_validation.sh: line 27: return: 1.2: numeric argument required
------------------------------------
4
------------------------------------

I get the same output when I try to multiple 1.2 by 1.2 as when I add them

The result is almost always wrong when performing calculations with negative numbers.

Example(Trying to multiply -1 by -1 gives 65025 ):

> 
-1
> 
-1
------------------------------------
65025
------------------------------------

I know I am listing quite a few things here. Posting a separate question for each incorrect calculation seemed kind of redundant so I listed a few here.


Update moved from where it was originally mistakenly added at the end of an answer:

@ Ed Morton I understand what you mean about the negative logic and I intend to fix that I just wanted to be able to actually get the calculations right. Thank you for pointing out my mistake with the return value I have solved the problem with this:

calculator(){
  persistant_get_menu_option "$header" "$options" "$option_descriptions"
  option=$?
  if [[ $option == "5" ]]
  then
    return 1
  else
    echo "> "
    num1=$(persistent_get_number)
    echo "> "
    num2=$(persistent_get_number)
    echo "------------------------------------"
    calculate "$option" "$num1" "$num2"
    echo "------------------------------------"
    return 0
   fi
}

And:

persistent_get_number(){
will_exit="0"
  while [ $will_exit == "0" ]
  do
    read -r user_input
    is_numerical "$user_input"
    if ! [ $? = 0 ]
    then
      echo "Input must be numerical"
    else
      will_exit="1"
    fi
  done
  echo $user_input
}
1

There are 1 best solutions below

3
Ed Morton On

Choosing 1 of the questions in your question:

return sets the exit status for a function (as opposed to what return means in C, for example) and an exit status is an integer. You're trying to return something that is not an integer, e.g. 1.2, hence the error message. e.g.

$ foo() { return 1.2; }
$ foo
-bash: return: 1.2: numeric argument required

You probably want to have the function produce output instead of setting it's exit status, e.g. something like this instead:

$ foo() { printf '%s\n' 1.2; }
$ var=$( foo )
$ echo "$var"
1.2

By the way, regarding:

is_numerical(){
  if ! [[ $user_input =~ whatever ]]
  then
    return 0
  else
    return 1
  fi
}

Negative logic is always harder to read than positive and it can lead to the indecipherable double-negatives so avoid using negative constructs like ! wherever possible. In your code above the "else" reads "it is NOT true that NOT $user_input matches whatever" - a double negative. Just write positive logic instead:

is_numerical(){
  if [[ $user_input =~ whatever ]]
  then
    return 1
  else
    return 0
  fi
}

Ditto for:

if ! [ $? = 0 ]
then
  echo "Input must be numerical"
else
  will_exit="1"
fi

vs:

if [ $? = 0 ]
then
  will_exit="1"      
else
  echo "Input must be numerical"
fi

and I'd guess other places in your code - think positive.