calculate sum or product based on input - linux

I am asking user to enter 2 numbers and s for sum, or "p" for product.
when I run the script I don't see any results
here is my script
#!/bin/bash
read -p "please enter two integers, s or p to calculate sum or product of this numbers: " num1 num2 result
if [ result == "s" ]
then
echo "num1+num2" | bc
elif [ result == "p" ]
then
echo $((num1*num2))
fi

You are comparing the string result, not the value of the variable result.
if [ "$result" = s ]; then
echo "$(($num1 + $num2))"
elif [ "$result" = p ]; then
echo "$(($num1 * $num2))"
fi
Inside $((...)), you can omit the leading $ because a string is assumed to be a variable name to be dereferenced.
There's no reason to use bc if you intend to restrict the inputs to integers.

To complement chepner's helpful answer, which explains the problem with the code in the question well, with a solution inspired by DRY[1]
:
# Prompt the user.
prompt='please enter two integers, s or p to calculate sum or product of this numbers: '
read -p "$prompt" num1 num2 opChar
# Map the operator char. onto an operator symbol.
# In Bash v4+, consider using an associative array for this mapping.
case $opChar in
'p')
opSymbol='*'
;;
's')
opSymbol='+'
;;
*)
echo "Unknown operator char: $opChar" >&2; exit 1
;;
esac
# Perform the calculation.
# Note how the variable containing the *operator* symbol
# *must* be $-prefixed - unlike the *operand* variables.
echo $(( num1 $opSymbol num2 ))
[1] Except for read's -p option, the solution is POSIX-compliant; it does, however, also work in dash, which is mostly a POSIX-features-only shell.

Related

Making a script to take input from a user and use error checking to test if two arguments were entered: Linux

My Linux class just started to begin scripting and I am having trouble with one of my questions. The question is as follows: Use a script to take two numbers as arguments and output their sum using bc.
This is my first script. To get the script to execute you need to do chmod +x filename
This is what I have so far:
#!/bin/bash
read -p "Enter in a numeric value: " num1
read -p "Enter in a second numeric value: " num2
if [ $# -ne 2 ] ; then
echo "Enter in two numeric arguments"
else
echo "The sum of the entered values are: "
echo "$num1 + $num2"|bc
fi
I keep running into an error. When I enter in two values it displays "Enter in two numeric arguments". It shouldn't do that because if I enter in two values, the if statement will evaluate to false and go to the else statement. Is my logic wrong or am I approaching this the wrong way?
You can use a while loop and break.
n=0
ex=""
while [ 1 ]
do
read -p "Enter read in a numeric value" num
n=$(( n + 1))
if [[ $n -eq 2 ]]
then
ex=${ex}${num}
echo "Sum of the two values are"
echo ${ex} | bc
break
else
ex=$num" + "
fi
done

Counting calculation operations and writing them into a file from a shell script

guys. I'm new to Linux and shell scripting in general and I have a question.
I have the following simple calculator:
input="yes"
while [[ $input = "yes" ]]
do
PS3="Press 1 for Addition, 2 for subtraction, 3 for multiplication and 4 for division: "
select math in Addition Subtraction Multiplication Division
do
case "$math" in
Addition)
echo "Enter first no:"
read num1
echo "Enter second no:"
read num2
result=`expr $num1 + $num2`
COUNTER=COUNTER+1
echo Answer: $result
break
;;
Subtraction)
echo "Enter first no:"
read num1
echo "Enter second no:"
read num2
result=`expr $num1 - $num2`
echo Answer: $result
break
;;
Multiplication)
echo "Enter first no:"
read num1
echo "Enter second no:"
read num2
result=`expr $num1 * $num2`
echo Answer: $result
break
;;
Division)
echo "Enter first no:"
read num1
echo "Enter second no:"
read num2
result=$(expr "scale=2; $num1/$num2" | bc)
echo Answer = $result
break
;;
*)
echo Choose 1 to 4 only!!!!
break
;;
esac
done
done
All I want is to be able to count the operations (meaning that for a successfull operation is +1, like "2 + 5 = 7" and some counter variable goes +1.. then something else and again +1) untill the user types something to stop the calculator. Then the counter variable (which holds the total number of operations performed) should be written inside a new file. How can I do this or can someone give me an example?
You can use a counter:
set count=0 before the loop
increment the counter after successful operation with ((count++)) after checking the status ($?) of the arithmetic operation
write the count to a file with printf "%d\n" "$count" > file
Not sure why you want to write to a new file each time. If that is the desired behavior, you can generate a new filename each time. Probably, you could name your file as operation.txt.N where N is the counter.
You can add Quit as an option that user can select:
PS3="Press 1 for Addition, 2 for subtraction, 3 for multiplication, 4 for division and 5 to Quit: "
select math in Addition Subtraction Multiplication Division Quit
... existing code here ...
And add this case:
Quit)
input=no
break
;;

Print error for missing argument

I am trying to write a script which will check number of arguments for first and second number; if both variable entered, it will do the calculation; if one argument is missing, it will print error message.
Here what I've done so far:
#!/bin/bash
echo -n "Enter the first number: "
read num1
echo -n "Enter the second number: "
read num2
if [ $# -le 1 ]; then
echo "Illegal number of arguments"
exit
else
echo "The sum is: " $(( num1 + num2 ))
fi
I am always getting error message even though I enter both of the numbers. What am I missing? Please help.
Test Your Assigned Variables, Not Positional Parameters
Your variables num1 and num2 aren't positional parameters, and so the special parameter $# is probably not what you think it is. You should change your conditional to check that both variables are set. For example:
declare -i num1 num2
read -p 'Enter the first number: ' num1
read -p 'Enter the second number: ' num2
if [ -z "$num1" ] || [ -z "$num2" ]; then
echo "Illegal number of arguments" >&2
else
echo "The sum is: $((num1 + num2))"
fi
Looks like you are messing up command line arguments and variables you are reading interactively. $# has nothing to do with variables you declared and/or read from command line. It is the number of command line arguments. You need to check the variables you attempted to read from console:
#!/bin/sh
echo -n "Enter the first number: "
read num1
echo -n "Enter the second number: "
read num2
[ -z $num1 ] || [ -z $num2 ] && echo "Illegal number of arguments" && exit 1
echo "The sum is: " $(( num1 + num2 ))
On the other hand, if you really want to check command line arguments, the script will be even simpler:
#!/bin/sh
[ -z $2 ] && echo "Illegal number of arguments" && exit 1
echo "The sum is: " $(( $1 + $2 ))
Here $1 refers to the first argument, $2 refers to the second argument and so on. $0 refers to the name of the script itself.
So, the way you have your program setup right now, it takes input through the read command, but that isn't the same as passing in an argument.
You pass an argument through the CLI, for instance:
./sum.sh 5 2 # => 7
Where sum.sh is the name of the file and 5 and 2 are your arguments.
So, the reason you keep getting "Illegal number of arguments" is because in bash the $# variable holds the number of arguments, but since you're reading the values in from the code, $# will always less than 1 because no arguments have been provided.
What I think you're looking for is something like this:
#!/bin/bash
if [ $# -le 1 ]; then
echo "Illegal number of arguments"
exit
else
echo "The sum is: " $(( $1 + $2 ))
fi
This article is pretty good if you want to learn more: http://www.bashguru.com/2009/11/how-to-pass-arguments-to-shell-script.html

How do I deal with empty user input in a Bash script?

When the script asks me for input, I get an error if I just press Return without typing in anything. How do I fix this?
Here's the script:
#!/bin/bash
SUM=0
NUM=0
while true
do echo -n "Pruefungspunkte eingeben ('q' zum Beenden): "
read SCORE
if test "$SCORE" == "q"
then echo "Durchschnittspunktzahl: $AVERAGE."
break
else SUM=`expr $SUM + $SCORE`
NUM=`expr $NUM + 1`
AVERAGE=`expr $SUM / $NUM`
fi
done
How about using good bash practices?
#!/bin/bash
sum=0
num=0
while true; do
read -erp "Pruefungspunkte eingeben ('q' zum Beenden): " score
if [[ $score = q ]]; then
echo "Durchschnittspunktzahl: $average."
break
elif [[ $score =~ ^-?[[:digit:]]+$ ]]; then
((sum+=10#$score))
((++num))
((average=sum/num))
else
echo "Bad number"
fi
done
Good practice:
don't use capitalized variable names
use the [[ builtin instead of the test builtin
don't use backticks, use (( to invoke shell arithmetic
to make sure the user inputs a number, check that a number was really entered. The line
elif [[ $score =~ ^-?[[:digit:]]+$ ]]; then
just does that (see regular expressions). Incidentally it completely solves your original problem, since an empty input will not pass through this test
to prevent problems if a user enters 09 instead of 9, force bash to interpret the input in radix 10. That's why I'm using (10#$score) instead of just score.
Use read with the -p (prompt) option, instead of the clumsy combo echo -n / read
This version is much more robust and well-written than yours. Yet, it still has problems:
will break if user needs large numbers
as shell arithmetic is used, only integers can be used. Moreover, the average given by this program is rounded: if you want the average of 1 and 2 you'll have 1.
To fix both problems, you'll probably want to use bc or dc. But that will be the purpose of another question. Or not.
Initialise $SCORE beforehand or handle empty input like you do in q case.
[[ -z "$SCORE" ]] && echo "\$SCORE is zero, e.g. \"\""
This will test if the variable SCORE is empty string.
You should also set AVERAGE=0 at the beginning.

Making a calculator that can loop - Bash [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I am trying to make a calculator. The user Enters number 1, chooses and operation, enters number 2, then chooses another operation or for the answer to be displayed.
eg.
1 + 1 =
or
1 + 1 + 2 + 1 =
Both of these should be possible.
read -p "what's the first number? " n1
PS3="what's the operation? "
select ans in add subtract multiply divide equals; do
case $ans in
add) op='+' ; break ;;
subtract) op='-' ; break ;;
multiply) op='*' ; break ;;
divide) op='/' ; break ;;
*) echo "invalid response" ;;
esac
done
read -p "what's the second number? " n2
ans=$(echo "$n1 $op $n2" | bc -l)
printf "%s %s %s = %s\n\n" "$n1" "$op" "$n2" "$ans"
exit 0
This is what I have written so far, but i cannot work out how to make it possible to let the user choose 'equals' or to loop back round to enter another operation. Any ideas what I can do to my code here? I have been stuck on this all day.
I dont want the user to enter the equation themselves, i want them to be choosing from a list.
Essentially you have to put a loop around that code so it reads a number then selects an operation repeatedly. Build up the formula. When the user selects "equals", break out of the outer loop and evaluate the formula. In pseudo-ish code:
formula=""
while true; do
get a number
formula+="$number"
select an operation
case $op in
...
equals) break 2 ;; # need to break out of 2 levels, the select and the while
esac
done
formula+="$op"
done
ans=$(bc -l <<< "$formula")
printf "%s = %s\n" "$formula" "$ans"
I would let the user enter the whole equation in one read. eg
read -p "enter equation" equate
ans=$(bc -l <<< "${equate%%=*})"
echo ${equate%%=*} = $ans
the <<< is a here string, the contents of the string are fed to cmd as stdin.
the %%=* in the equate variable strips of any thing after a = that may have been put in.
#!/bin/bash
read -p "what's the first number? " n1
PS3="what's the operation? "
select ans in add subtract multiply divide equals; do
case $ans in
add) op='+' ; break ;;
subtract) op='-' ; break ;;
multiply) op='*' ; break ;;
divide) op='/' ; break ;;
*) echo "invalid response" ;;
esac
done
read -p "what's the second number? " n2
ans=$(echo "$n1 $op $n2" | bc -l)
printf "%s %s %s = %s\n\n" "$n1" "$op" "$n2" "$ans"
exit 0

Resources