I am trying to create a script file in Linux that acts as a basic calculator. It needs to pass 3 parameters or no parameters.
If it has 3 parameters, it should be able to execute like this.
./mycalc 1 + 2
the sum of 1 plus 2 equals 3
But if it does not have any parameters a menu should display asking for subtraction, addition, or exit.
How would this layout look? I keep trying but whenever I run it it gives me errors saying I need to enter the parameters and then after the error the menu displays.
op="$2"
if [ $op == "+" ]
then
sum=$(expr $1 + $3)
echo "The sum of $1 plus $3 equals $sum"
elif [ $op == "-" ]
then
diff=$(expr $1 - $3)
echo "The sum of $1 minus $3 equals $diff"
else
while [ $ch != "X" ] || [ $ch != "x" ]
do
read -p "C) Calculation
X) Exit" ch
Here's a "cute" answer. I'll give you some feedback on your code later.
#!/bin/bash
case $2 in
+|-) :;;
*) echo "unknown operator: $2"; exit 1;;
esac
declare -A ops=(
["-"]=minus
["+"]=plus
)
printf "%d %s %d equals %d\n" "$1" "${ops["$2"]}" "$3" "$(bc <<< "$*")"
Here's a rewrite, hopefully demonstrating a few useful techniques and good practices
#!/bin/bash
user_args () {
case $2 in
+) echo "The sum of $1 plus $3 equals $(( $1 + $3 ))" ;;
-) echo "The sum of $1 minus $3 equals $(( $1 - $3 ))" ;;
*) echo "I don't know what to do with operator '$2'" ;;
esac
}
interactive () {
PS3="Select an operator: "
select choice in plus minus exit; do
case $choice in
plus) operator="+"; break ;;
minus) operator="-"; break ;;
exit) exit;;
esac
done
read -p "First value: " first
read -p "Second value: " second
echo "The sum of $first $choice $second equals $(( $first $operator $second ))"
}
if (( $# == 3 )); then
user_args "$#"
else
interactive
fi
use of functions for modularity
case statement for easily expanded branching
select statement to generate the menu and enforce valid input
bash's built-in arithmetic expressions
passing arguments with "$#"
quoting variables everywhere the need to be quoted
Command line parameters are referenced as $1, $2, $3...
($1 for first arg, $2 for second ...)
you can test if parameters are not null with :
if [ -z "$1" ]
then
echo "No argument supplied"
fi
Related
I am trying to make a simple menu-driven calculator script. I am trying to make it such that after selecting A (add) or B (subtract) from the menu, it'll call the function and display the corresponding message when:
The parameters entered when function called is greater than 3
No parameters are entered when the function is called
The operator entered when the function is called is neither "+" or "-"
Right now it is doing the parameter check when I call the script ./mycalc.sh
executing the script
Am not sure how to make it check parameters after the function is called?
#!/bin/bash
display() {
echo "Calculator Menu"
echo "Please select an option between add, subtract or exit"
echo "A. Add"
echo "B. Subtract"
echo "C. Exit"
}
#initialize choice n
choice=n
if [[ $# -ne 3 ]]
then echo " You have not entered 3 parameters"
exit 1
fi
if [ $# -eq 0 ]
then
echo " You have not entered any parameters, please input 3. "
fi
if [[ $2 != [+-] ]]
then
echo " Please enter an add or subtract operator."
exit 1
fi
add() {
echo " The sum of $one + $three equals $(( $one $op $three ))"
}
subtract () {
echo " The difference of $one - $three equals $(( $one $op $three )) "
}
while [ $choice != 'C' ]
do display
read choice
if [ $choice = 'A' ]
then
read -p "Please enter two operands and the operator '+': " one op three
add $one $op $three
elif [ $choice = 'B' ]
then
read -p " Please enter two operands and the operator '-': " one op three
subtract $one $op $three
elif [ $choice = 'C' ]
then
echo "Thank you for using this program. The program will now exit."
fi
done
sleep 3
exit 0
if [ $# > 3 ]
then
echo " You need to input 3 parameters. "
fi
Within [...], the > is simply redirection. You'll find an empty file named 3 in your current directory.
Since this is an error condition for your script, you'll want to exit:
if [[ $# -ne 3 ]]; then
echo "usage: $0 operand1 operator operand2" >&2
exit 1
fi
And to test the operator, there are many ways.
case, but it's a bit verbose
case $2 in
'+'|'-') : ;;
*) echo "Operator must be + or -" >&2
exit 1
;;
esac
Within [[...]] the == and != operators are pattern matching operators
if [[ $2 != [+-] ]]; then
echo "Operator must be + or -" >&2
exit 1
fi
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" ```
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.
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
I am having trouble with this while loop. Here is the error I am getting currently : [: : unary operator expected (on line 3)
Here is my code:
#!/bin/bash
while [ "$option" <= 3 ]; do
echo "1) View 'HelloWorld' Text";
echo "2) View 'MyName' Text";
echo "3) View 'HappyFall' Text";
echo "4) Exit\n";
read -p "Please choose your option using numbers 1-4: " option;
if [ "$option" == 1 ]
then
cat HelloWorld
elif [ "$option" == 2 ]
then
cat MyName
elif [ "$option" == 3 ]
then
cat HappyFall
else
echo "Exiting...";
fi
done
<= is not a valid comparison in bash scripting, but -le (less than or equal) is.
There are two different types of comparison in scripting: those for strings, and those for numbers.
Stings typically use == or != for equal or not equal.
Integers, however, use:
-eq equal
-ne not equal
-lt less than
-le less than or equal
-gt greater than
-ge greater than or equal
Here's a more comprehensive list:
http://mywiki.wooledge.org/BashGuide/TestsAndConditionals#Conditional_Blocks_.28if.2C_test_and_.5B.5B.29
Error aside, this is a good use case for the select command:
options=(
"View 'HelloWorld' Text"
"View 'MyName' Text"
"View 'HappyFall' Text"
"Exit"
)
select option in "${options[#]}"; do
case $REPLY in
1) cat HelloWorld ;;
2) cat MyName ;;
3) cat HappyFall ;;
*) break ;;
esac
done
I want to create a select menu in it, like this:
echo "Choose your option:"
1) Factorial Calculation
2) Addition Calculator
3) Quit
And I have some shell scripts;
Factorial
./fact.sh
#!/bin/bash
fact=1
#taking input from user
echo -e "enter a number"
read n
#if enter value less than 0
if [ $n -le 0 ] ; then
echo "invalid number"
exit
fi
#factorial logic
if [ $n -gt 0 ] ; then
for((i=$n;i>=1;i--))
do
fact=`expr $fact \* $i`
done
fi
echo "The factorial of $n is $fact"
Addition
./add.sh
#!/bin/bash
#function to add two numbers
add()
{
x=$1
y=$2
echo -e "Number entered by u are: $x and $y"
echo "sum of $1 and $2 is `expr $x + $y` "
}
# main script
echo "enter first number"
read first
echo "enter second number"
read sec
#calling function
add $first $sec
echo "end of the script"
I have to create a menu, how should I proceed?
You can use select.
For your example:
select option in Factorial Addition Quit; do
case "$option" in
"Factorial")
echo "Factorial"
break ;;
"Addition")
echo "Addition"
break ;;
"Quit") exit ;;
esac
done
When I write parameters they should be checked in a case and then added in a list if u type -c in front the parameter should be in uppercase and if u type -4 the parameter should appear 4 times
#!/bin/bash
function forloop {
for each in naam
do
echo $naam zegt je dat...
echo ""
echo
let "x+=1"
done
}
function helper {
echo "use names only."
echo "if u type -c before a word then it wil be shown in capital"
echo "if u type -(number) before a word the word will be shown (number)times "
}
naam=null
x=0
while [ $x -le 4 ]
do
case $1 in
-c) echo "${2 ^^}";shift ;;
help) helper ;shift;naam=helper;;
-4) $1x-1
exit ;shift;;
esac
naam=" $1"
shift
forloop
done
exit
#!/bin/bash
if [ -z "$1" ]
then
echo "use names only."
echo "if you type -c before a word, then it will be shown in capitals"
echo "if you type -NUMBER before a word, the word will be shown NUMBER times"
exit
fi
while [ $1 ]
do
case $1 in
-c) shift; <<<$1 tr '[:lower:]' '[:upper:]';;
-[0-9]*)for ((count=$1; count++; )) do echo $2; done; shift;;
*) echo $1
esac
shift
done