I'm having an issue getting a a simple y/n question to work. Consider the following code:
echo "Hi there"
read ans
if [[ $ans != "y" || $ans != "Y" || $ans != "YES" || $ans != "yes" ]]; then
echo "Foo"
exit 0
fi
I've looked at ā I would argue ā some of the more informative answers on StackOverflow for advice: Simple logical operators in Bash
I've tried all different types of variations such as:
if [[ ($ans != "y" || $ans != "Y" || $ans != "YES" || $ans != "yes") ]]; then
echo "Foo"
exit 0
fi
if [[ ($ans != "y*" || $ans != "Y*" || $ans != "YES*" || $ans != "yes*") ]]; then
echo "Foo"
exit 0
fi
if [[ ($ans != "y") || ($ans != "Y") || ($ans != "YES") || ($ans != "yes") ]]; then
echo "Foo"
exit 0
fi
Regardless of why I type in any of these cases, it automatically fails and I'm not sure why. If anyone has a better way to handle y/n answers then please let me know! Ideally I would like to use pattern matching (like I might do with Perl) but I'm not entirely sure the best way/most efficient way to accomplish a simple y/n question.
You need to use && instead of ||. As it stands you're saying if it's not equal to any of those possibilities, then execute the "then" block. You mean to say if it's not equal to all of them, then execute the "then" block. That requires &&.
You can use:
echo "Hi there"
read ans
case "$ans" in
y|Y|YES|yes)
;;
*)
echo "Foo"
exit 0
;;
esac
The logic needs to be adjusted:
echo "Hi there"
read ans
if ! [[ "$ans" == "y" || "$ans" == "Y" || "$ans" == "YES" || "$ans" == "yes" ]]; then
echo "Foo" # User answered no
exit 0
fi
The will echo "Foo" only if the answer was not one of "y", "Y" or "YES". By contrast, consider the original logic:
[[ $ans != "y" || $ans != "Y" || $ans != "YES" || $ans != "yes" ]]
At least one of these tests will be true regardless of what the user's answer is.
Using the case statement
You might consider using the case statement to analyze the user's answer:
read -p "Hi there: " ans
case "$ans" in
[yY]*) ;;
[nN]*) echo "Foo" ; exit 0 ;;
*) echo "You were supposed to answer yes or no" ;;
esac
Try read ans, not read $ans.
Related
When running the code below, Iād like to read a user-input answer until it is one of the 6 possible answers ([abcABC]), else continue with the next loop.
However, it does not exit the while loop when I enter one of the accepted answers.
I tried [] and [[]] for the conditions, I tried to place all of the conditions into one pair of square brackets, I tried to use | and ||, none of them worked as expected.
while [ "$ans" != "a" ] || [ "$ans" != "A" ] || [ "$ans" != "b" ] || \
[ "$ans" != "B" ] || [ "$ans" != "c" ] || [ "$ans" != "C" ]; do
read ans
case $ans in
[aA]) echo "aA" ;;
[bB]) echo "bB" ;;
[cC]) echo "cC" ;;
*) echo "Try again." ;;
esac
done
It should read in loop until one of the accepted answer is given; then it should continue with the following code (if any).
Your loop condition is always true, and Bash correctly loops forever.
Consider a simplified version:
[ "$ans" != "a" ] || [ "$ans" != "b" ]
If ans is a then it becomes false || true which is true.
If ans is b then it becomes true || false which is true.
If ans is x then it becomes true || true which is true.
You wanted && instead of ||. Alternatively, do it in a single comparison: [[ "$ans" != [aAbBcC] ]]
I have this bash script that receives me the error mentioned below. Do you have any explanations?
#! /bin/bash
first=1
second=1
result=2
contor=2
fib()
{
contor=$1
if [ $contor -eq $n ]
then
echo result
else
let $contor++
let result=$first+$second
first=$second
second=$results
fib $contor
fi
}
read n
result=$(fib 2)
echo $reusult
I see at least three problems here:
let $contor++ will actually substitute the value of contor, meaning the expression will be something like let 42++, which is invalid. Get rid of the $.
second=$results will set second to something other than what's in result, which will probably cause an issue when you try to execute let result=$first+$second. Use the correct variable name.
You print the variable reusult at the end, again a mis-spelling.
The way I found those issues is the same way you (or anyone, for that matter) should be finding them in bash, by temporarily putting the following lines at the top of your script to aid debugging:
set -e # stop on error
set -x # echo each interpreted line before execution.
There's are a great help when trying to figure out where the problems lie.
For what it's worth, Fibonacci is usually a bad candidate for recursion since you generally have to calculate things more than once.
That's because calculating fib(1000) as fib(999) + fib(998) usually entails a massive duplication of effort on either side of that + symbol.
That's not actually the case with your code because, while the code is calling itself, the leakage of information across levels really makes it an iterative solution - at no stage are you getting the result of fib(n-1) and fib(n-2) to calculate fib(n).
So, since you have an iterative solution anyway, you may as well remove the last vestiges of recursion and use something like:
#! /bin/bash
fib() {
# Validate input argument to be integer 1...92.
num=$1
if [[ ! ${num} =~ ^[1-9][0-9]*$ ]] ; then
echo "Invalid input '${num}'."
return
fi
if [[ ${num} -gt 92 ]] ; then
echo "Invalid input '${num}'."
return
fi
# First two Fib's are both one.
if [[ ${num} -le 2 ]] ; then
echo "1"
return
fi
# For other Fib's, just iterate keeping previous two.
grandparent=1 ; parent=1 ; child=2
((num -= 3))
while [[ ${num} -gt 0 ]] ; do
((grandparent = parent))
((parent = child))
((child = grandparent + parent))
((num--))
done
echo ${child}
}
# Rather rudimentary test harness.
[[ "$(fib x)" != "Invalid input 'x'." ]] && echo Fail A && exit
[[ "$(fib -7)" != "Invalid input '-7'." ]] && echo Fail B && exit
[[ "$(fib 0)" != "Invalid input '0'." ]] && echo Fail C && exit
[[ "$(fib 93)" != "Invalid input '93'." ]] && echo Fail D && exit
[[ "$(fib 1)" != "1" ]] && echo Fail E && exit
[[ "$(fib 2)" != "1" ]] && echo Fail F && exit
[[ "$(fib 3)" != "2" ]] && echo Fail G && exit
[[ "$(fib 4)" != "3" ]] && echo Fail H && exit
[[ "$(fib 5)" != "5" ]] && echo Fail I && exit
[[ "$(fib 6)" != "8" ]] && echo Fail J && exit
[[ "$(fib 20)" != "6765" ]] && echo Fail K && exit
[[ "$(fib 50)" != "12586269025" ]] && echo Fail L && exit
[[ "$(fib 92)" != "7540113804746346429" ]] && echo Fail M && exit
# User test.
read -p "Which Fibonacci number (>= 1)? " n
result=$(fib $n)
echo $result
You'll notice the input is restricted to the range 1..92. That's because, at some point (92 is the last one that works for me), bash will start giving strange results because of limits of its internal data types.
I am having issues with this while loop where I am trying to check if the 'choice' value read from the user's input is NOT EQUAL to either 'yes' or 'false'. Right now the validation catches anything that isn't 'yes' or 'no'.
The problem is that, the validation ALSO enters the while loop when the user enters 'yes' or 'no'...
Does anyone have any idea about what is going on and how I could make this thing work?
while read choice && ([[ "$choice" != "yes" ]] || [[ "$choice" != "no" ]]); do
echo -n "'$choice' is invalid choice, enter 'yes' or 'no': ";
done
Thanks in advance!
You have a typo. It should be ([[ "$choice" != "yes" ]] && [[ "$choice" != "no" ]])
while read choice && ([[ "$choice" != "yes" ]] && [[ "$choice" != "no" ]]); do
echo -n "'$choice' is invalid choice, enter 'yes' or 'no': ";
done
I have just started learning to write bash scripts. This a simplified form of what I want to write.
The problem is despite of the input, it prints only "YES".
#! /usr/bin/bash
read input
if (("$input"== "y" || "$input" == "Y"))
then
echo "YES";
elif (("$input" == "n" || "$input" == "N"))
then
echo "NO";
else
echo "Not a valid input!!"
fi
Use [[ instead of (( like,
if [[ "$input" == "y" || "$input" == "Y" ]]
and also there must be a space exists before == operator.
ie,
input="n"
if [[ "$input" == "y" || "$input" == "Y" ]]
then
echo "YES";
elif [[ "$input" == "n" || "$input" == "N" ]]
then
echo "NO";
else
echo "Not a valid input!!"
fi
You could use regular expression also for condition checking purpose.
if [[ "$input" =~ ^[yY]$ ]]
then
echo "YES";
elif [[ "$input" =~ ^[nN]$ ]]
then
echo "NO";
else
echo "Not a valid input!!"
fi
When you automaticly convert the input to lowercase (using typeset), you do not have to bother with the the uppercases.
When you use an elif, always think 10 seconds for another solution. In this case you might want to use a "switch", in shell written as a case-statement:
#!/usr/bin/bash
typeset -l input
read input
case ${input} in
"y") echo "Yes";;
"n") echo "NO";;
*) echo "Not a valid input!!";;
esac
I am not looking for a different way to accomplish the apparent intention. I'm looking to understand why this exact syntax is not working.
[root#lvs ~]# while true;do
> echo "Would you like the script to check the second box ([y]n)?"
> read ans
> if [ "$ans" == "n" ];then
> echo
> echo "bye"
> exit
> elif [ "$ans" != "" -o "$ans" != "y" ];then
> echo "Invalid entry..."
> else
> break
> fi
> done
Would you like the script to check the second box ([y]n)? **"Should have continued"**
Invalid entry...
Would you like the script to check the second box ([y]n)? **"Should have continued"**
y
Invalid entry...
Would you like the script to check the second box ([y]n)? **"Correct behavior"**
alskjfasldasdjf
Invalid entry...
Would you like the script to check the second box ([y]n)? **"Correct behavior"**
n
bye
Here's a reference that's identical to so many others i found. I understand what it's doing, it's using the non logical's for AND and OR when everything I've read said that it should be using logical bools.
http://www.groupsrv.com/linux/about140851.html
Ok so here it is, with Nahuel's suggestion behaving how I had originally expected it to:
[root#lvs ~]# while true;do
> echo "Would you like the script to check the second box ([y]n)?"
> read ans
> if [ "$ans" = "n" ];then
> echo
> echo "bye!"
> exit
> elif [ "$ans" != "" -a "$ans" != "y" ];then
> echo "Invalid entry..."
> else
> break
> fi
> done
Would you like the script to check the second box ([y]n)?
asdfad
Invalid entry...
Would you like the script to check the second box ([y]n)?
[root#lvs ~]# while true;do
> echo "Would you like the script to check the second box ([y]n)?"
> read ans
> if [ "$ans" = "n" ];then
> echo
> echo "bye!"
> exit
> elif [ "$ans" != "" -a "$ans" != "y" ];then
> echo "Invalid entry..."
> else
> break
> fi
> done
Would you like the script to check the second box ([y]n)?
y
[root#lvs ~]# while true;do
> echo "Would you like the script to check the second box ([y]n)?"
> read ans
> if [ "$ans" = "n" ];then
> echo
> echo "bye!"
> exit
> elif [ "$ans" != "" -a "$ans" != "y" ];then
> echo "Invalid entry..."
> else
> break
> fi
> done
Would you like the script to check the second box ([y]n)?
n
logout
The problem is that : [ "$ans" != "" -o "$ans" != "y" ] is always true because of the or and the negation. $ans cannot be equal to "" and to "y".
Try replace these lines
if [ "$ans" == "n" ];then
elif [ "$ans" != "" -o "$ans" != "y" ];then
by these
if [ "$ans" = "n" ];then
elif [ "$ans" != "" -a "$ans" != "y" ];then
or these
if [[ $ans == n ]];then
elif [[ $ans != "" && $ans != y ]];then
The easier is to do is a case:
case $ans in
y) echo "yes"
;;
n) echo "no"
;;
*)
;;
esac
also break must be used only in a for or while loop, or in a select but it is missing in your post .
I don't really understand, why do you use -o in the elif. I would use "||" or "OR" operator. When you use two conditions in if, you should use double [[ and ]].
So if you use:
elif [[ "$ans" != "" || "$ans" != "y" ]];then
it works fine.
also logically its a flawed way of doing things.
firstly using case would be best in this scenario, secondly you are looking for == n then stating if it is blank or not equal to yes - so although no is caught out in first if statement in theory it would still meet second criteria
surely the most logical way to ensure input is 100% would be
if [ "$ans" == "n" ];then
echo
echo "bye"
exit
elif [ "$ans" == "y" ];then
echo Yes
break;
else
echo "Invalid entry... >$ans<"
fi