bash while loop comparison not evaluating - bash

I am trying to understand the difference between coding for if and while statements in bash.
In the script below, if I use IF I get the expected result using == OR -eq etc. However with the while loop each while seems to evaluate as true for all tests. While loops 1 & 3 should fail, correct?
A != B,
A == B,
A == C
I have tried different combinations of [ "" "" ] or [[ "" "" ]] etc.
What is the correct syntax for while
thx
Art
#!/bin/sh
set -x
myVarA="abc"
myVarB="abc"
myVarC="def"
while [[ ${myVarA} != ${myVarB} ]]
echo "A does not equal B"
i=$(($i+1))
do sleep 1s
break
done
while [[ ${myVarA} == ${myVarB} ]]
echo "A does equal B"
i=$(($i+1))
do sleep 1s
break
done
while [[ ${myVarA} == ${myVarC} ]]
echo "A does equal C"
i=$(($i+1))
do sleep 1s
break
done
renders the following,
+ myVarA=abc
+ myVarB=abc
+ myVarC=def
+ [[ abc != abc ]]
+ echo 'A does not equal B'
A does not equal B
+ i=1
+ sleep 1s
+ break
+ [[ abc == abc ]]
+ echo 'A does equal B'
A does equal B
+ i=2
+ sleep 1s
+ break
+ [[ abc == def ]]
+ echo 'A does equal C'
A does equal C
+ i=3
+ sleep 1s
+ break

Your do keywords are wildly misplaced.
Everything between while and do is the condition list.
So your condition list in the first block is
[[ ${myVarA} != ${myVarB} ]]; echo "A does not equal B"; i=$(($i+1))
which, as you might imagine, evaluates as true since i=$(($i+1)) evaluates as true.
Similarly for the other blocks.
Move the do to the end of the while lines
while [[ ${myVarA} != ${myVarB} ]]; do
: do something
done

Related

shell script issue with positional arguments and if else

I'm trying to write a script that will accept positional arguments (command-line arguments) that specify a mathematical operation (add, sub, mult, div, mod) and two values. The script is supposed to get this input, compute the result, and display the answer. I'm not sure where I went wrong because when I try to run my script, this is what I get
./trial.sh mod 15 10
./trial.sh: 6: if [[ mod == add ]] then: not found
Result: 25
./trial.sh: 9: elif [[ mod == sub ]] then: not found
Result: 5
./trial.sh: 12: elif [[ mod == mult ]] then: not found
Result: 150
./trial.sh: 15: elif [[ mod == div ]] then: not found
Result: 1
./trial.sh: 18: elif [[ mod == mod ]] then: not found
Result: 5
./trial.sh: 21: else: not found
error
./trial.sh: 23: fi: not found
This is the code I used that presented that result
#!/bin/bash
a=$1
b=$2
c=$3
result=0
" if [[ $a == add ]] then"
result=$((b + c))
echo "Result: " $result
" elif [[ $a == sub ]] then"
result=$((b - c))
echo "Result: " $result
" elif [[ $a == mult ]] then"
result=$((b * c))
echo "Result: " $result
" elif [[ $a == div ]] then"
result=$((b / c))
echo "Result: " $result
" elif [[ $a == mod ]] then"
result=$((b % c))
echo "Result: " $result
" else"
echo "error"
"fi"
Your sample code is incorrect in several ways. You wrapped some of your statements in double-quotes to make the errors stop, but this is a bit like taking the engine out of a car because it's making a weird noise.
Here are three things you can do immediately to make your code work:
take away the double-quotes so that your code is code statements again and not strings. If you want to temporarily disable some shell code, put # before it to comment it out, e.g.,
# don't wrap broken code in double-quotes:
"if[x= 1]] then rm -rf /"
# instead, comment it out, like this:
# if[x= 1]] then rm -rf /
in bash if...then, a newline or semicolon is required before then , e.g.,
if [[ $a == 1 ]] then # wrong
if [[ $a == 1 ]]; then # ok
if [[ $a == 1 ]] # also ok
then
change #!/bin/sh to #!/bin/bash because many variants of sh don't support double-bracket [[ tests.

bash if statement with strings always evaluates to true

I am getting started with bash and am having trouble with if statements.
Why does the following script:
#!/bin/bash
read C
if (( $C == 'Y' )) ; then
echo "YES"
elif (( $C == 'N' )) ; then
echo "NO"
fi
Seem to print YES no matter what value $C takes on.
Strings inside the arithmetic statement ((...)) are recursively expanded until you either get an integer value (including 0 for an undefined parameter) or a string that causes a syntax error. Some examples:
# x expands to y, and y expands to 3
$ x=y y=3
$ (( x == 3 )) && echo true
true
$ x="foo bar"
$ (( x == 3 ))
bash: ((: foo bar: syntax error in expression (error token is "bar")
# An undefined parameter expands to 0
$ unset x
$ (( x == 0 )) && echo true
true
In your case, $C expands to some undefined parameter name, and both it and Y expand to 0, and 0 == 0.
For string comparison, use [[ ... ]] instead.
if [[ $C == Y ]]; then
Yep, as #larsks mentioned, you need the square brackets. Try this full version:
#!/bin/bash
read C
if [[ ${C} == 'Y' ]]; then
echo "YES"
elif [[ ${C} == 'N' ]]; then
echo "NO"
fi
Here is the right format.
#!/bin/bash
read C
if [[ $C == 'Y' ]]
then
echo "YES"
elif [[ $C == 'N' ]]
then
echo "NO"
fi

bash syntax error expected operad

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.

bash [[ for numerical comparison?

We have some scripts that do things like
e=$?
if [[ $e == 123 ]]; then exit 1; fi
They're more complicated than that, it's just an example. My question is using double brackets acceptable to make numerical comparisons this way, is there any disadvantage? I would think it should be double parentheses if (( $e == 123 )) but I don't want to go changing a lot of scripts over nothing.
Thanks
There are a lot of key differences doing it, because == checks for exact string equality, but -eq evaluates both expressions arithmetically before checking for equality.
$ [[ " 1 " -eq 1 ]] && echo equal || echo not
equal
$ (( " 1 " == 1 )) && echo equal || echo not
equal
$ [[ " 1 " = 1 ]] && echo equal || echo not
not
Also, the empty string happens to be numerically equal to zero:
$ [[ "" -eq 0 ]] && echo equal || echo not
equal
$ [[ "" == 0 ]] && echo equal || echo not
not
And a whole other class of differences appears when you bring the comparison operators in - considering < vs -lt, for instance:
$ [[ 2 -lt 10 ]] && echo less || echo not
less
$ (( 2 < 10 )) && echo less || echo not
less
$ [[ 2 < 10 ]] && echo less || echo not
not
This is because the string 2 is alphabetically after the string 10 (since 1 comes before 2), but the number 2 is numerically less than the number 10.
Credits to the original cross site duplicate, with a few updates Is there any major difference when comparing a variable as a string or as an int?
The verdict is to use $((..)) for arithmetic comparisons strictly to avoid interpreting the operands as strings.

Bash script ignores the negative prefix on negative numbers

a=4;
b=7;
c=5;
x =[ a-b ]
if (x -gt c) then {
echo "x is greater"
} else {
echo " something"
}
I want to compare x and c ignoring the negative prefix of c.
I'm assuming you meant "negative prefix of x". There are a ton of errors in your code, are you sure you're writing in bash?
#!/bin/bash
typeset a=4 b=7 c=5
x=$(( a - b ))
x=${x//-/}
if [[ x -gt c ]]; then
echo "x is greater"
else
echo " something"
fi

Resources