0

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
}
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
Daniel
  • 23
  • 4
  • Gotcha, can you give me an example of a line where this a done and the way it should be done? I am aware it's not Python but they can do some of the same things I'm just learning the differences as I go. – Daniel May 29 '23 at 03:52
  • 3
    Regarding "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.". Posting multiple questions in 1 will get your question closed as needing focus, and posting multiple separate but similar questions at once will get them closed as duplicates. Ask 1 question about 1 thing then see if you can figure out the rest of your problems once you get that answer and only ask your next question if necessary after trying to apply what you learn from the first one. – Ed Morton May 29 '23 at 11:09
  • 2
    And make sure to create and post a [mcve] about just that 1 thing, not whatever longer script (or scripts!) you have lying around that contains that problem somewhere within it. Also when you get an error message like `utilities/input_validation.sh: line 27: ...` that's telling you which line in which file has the problem, make sure to pass that information along. In your current question you didn't tell us the names of your files so we don't even know which script is producing the error message. – Ed Morton May 29 '23 at 11:10
  • Note the "minimal" part of the [mre] definition. The error message tells you which line the problem is on; you should be able to cut the question down to be _just_ about that one line. (`bash -x yourscript` will run with logging so you can see actual variables' values at each point in execution, to better isolate the specific place where things went off the rails). – Charles Duffy May 29 '23 at 23:14

1 Answers1

1

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.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • Might consider `is_numerical() { [[ $user_input =~ whatever ]]; }` -- no reason for explicit `return`s at all. – Charles Duffy May 29 '23 at 23:15
  • @Daniel, don't edit responses to answers into those answers. An answer's text is expected to try to generally reflect its author's intent. (And `if is_numerical "$user_input"; then` is better than using `$?` at all). – Charles Duffy May 29 '23 at 23:15
  • @Daniel, ...see [Why is testing `$?` to see if a command succeeds or not an antipattern?](https://stackoverflow.com/questions/36313216/why-is-testing-to-see-if-a-command-succeeded-or-not-an-anti-pattern) – Charles Duffy May 29 '23 at 23:17