Arithmetic expression inside [[ - bash

I have this code:
fruit=apple
flag=0
[[ $fruit = "apple" && ((flag == 0)) ]] && echo "1"
[[ $fruit = "apple" && (($flag == 0)) ]] && echo "2"
[[ $fruit = "apple" && ((! flag)) ]] && echo "3"
[[ $fruit = "apple" && ((! $flag)) ]] && echo "4"
All of them are expected to echo something. However, only the second statement works properly:
[[ $fruit = "apple" && (($flag == 0)) ]] && echo "2"
Why is this? Won't arithmetic expressions work properly inside [[ ]]?

The == works the same as = - it is a string comparision.
The ( ) inside [[ ]] is used to nest expressions. Ex. [[ a = a && (b = b || c = d) ]]
A non empty string is true. So [[ some_string ]] or [[ "some other string" ]] returns true. [[ "" ]] or [[ '' ]] returns false.
The ! is negation.
[[ $fruit = "apple" && ((flag == 0)) ]]
First the expansion of $fruit happens. So it becomes: [[ "apple" = "apple" && ( ( "flag" == "0" ) ) ]]. The ( ) are just braces, this is not arithemtic expansion. The string "apple" is equal to "apple", but the string "flag" is not equal to string "0", so it always returns with false.
[[ $fruit = "apple" && (($flag == 0)) ]]
Because $flag is expanded, the [[ sees: [[ "apple" = "apple" && ((0 == 0)) ]]. So the comparision happens to work, however == is doing string comparision.
[[ $fruit = "apple" && ((! flag)) ]]
The flag is a nonempty string, so it evaulates to true. The ! flag evaulates to false.
[[ $fruit = "apple" && ((! $flag)) ]]
First $flag is expanded to 0. As 0 is a nonempty string, it evaluates to true. The ! 0 evaluates to false. The ((! 0)) is false, so the whole expression returns nonzero.
Won't arithmetic expressions work properly inside [[ ]]?
No, arithmetic expressions will not work inside [[ ]] the same way [[ echo 1 ]] does not work. The echo 1 ]] are arguments of [[ builtin, not a standalone command. The same way [[ (( 0 )) ]] the (( 0 )) ]] are interpreted as arguments to [[.

((...)) is an arithmetic command (or statement); the arithmetic expresson is $((...)). You want
[[ $fruit = "apple" ]] && ((flag == 0)) && echo 1
# etc.
[[ $fruit = apple && $((flag == 0)) ]] would fail because [[ ... ]] would simply treat the evaluation of the arithmetic expression as a non-empty string (which is always true) rather than a boolean value.
For completeness, you could use a single [[ ... ]] command:
[[ $fruit = apple && flag -eq 0 ]] && echo 1
# etc

Related

Why is it saying integer expected? [duplicate]

This question already has answers here:
How do I test if a variable is a number in Bash?
(40 answers)
Closed 2 years ago.
#!/bin/bash
if [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" == "" ]
then
echo This is empty or does not have all 3 parameters
exit
elif [ "$1" -lt 0 ]
then
echo This aint a number
exit
fi
Trying to run a script it is suppose first check if 3 positional parameters were entered,
secondly check if the input are numbers and then display the largest. I got the first if statement to work but when I input a string for the first parameter to test the elif statement an error that says integer expected.
you have to review the regex to check if string only contains digits, but you may try this:
#!/bin/bash
if [[ "$1" == "" ]] || [[ "$2" == "" ]] || [[ "$3" == "" ]]
then
echo "This is empty or does not have all 3 parameters"
exit
elif ! [[ "$1" =~ ^[0-9]+$ ]]
then
echo "This aint a number"
exit
fi
Note that [[ is actually a command/program that returns either 0 (true) or 1 (false). Any program that obeys the same logic (like all base utils, such as grep(1) or ping(1)) can be used as condition :
[[ -z STRING ]] #Empty string
[[ -n STRING ]] #Not empty string
[[ STRING == STRING ]] #Equal
[[ STRING != STRING ]] #Not Equal
[[ NUM -eq NUM ]] #Equal
[[ NUM -ne NUM ]] #Not equal
[[ NUM -lt NUM ]] #Less than
[[ NUM -le NUM ]] #Less than or equal
[[ NUM -gt NUM ]] #Greater than
[[ NUM -ge NUM ]] #Greater than or equal
[[ STRING =~ STRING ]] #Regexp
(( NUM < NUM )) #Numeric conditions
also to check if the string contains only digits/numerical cheracter
[[ "$1" =~ ^[0-9]+$ ]]
Please use a regex to check for number
#!/bin/bash
if [ $1 == "" ] || [ $2 == "" ] || [ $3 == "" ]
then
echo This is empty or does not have all 3 parameters
exit
elif ! [[ $1 =~ ^[0-9]+$ ]]
then
echo This aint a number
exit
fi

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 if clause with and or combined

I'm trying to be smart but it doesn't work. Can anybody help me to do this a bit simpler?
if [[ "${DATUM}" == "${today}" && "${STUNDE}" == "${HH24}" ]] || [[ "${DATUM}" == "${today}" && "${STUNDE}" == "20" ]] ||
[[ "${DATUM}" == "${today}" && "${STUNDE}" == "" && "20" == "${HH24}" ]]; then
Is there a way to combine it?
Your code can be translated to:
(C1 and C2) or (C1 and C3) or (C1 and C4 and C5)
Applying boolean arithmetics you can simplify it as:
C1 and (C2 or C3 or (C4 and C5))
This said, you can add a nested if statement to, first, check the C1 condition and, second, check the other conditions. It does not simplify the code a lot but here it is:
if [ "${DATUM}" = "${today}" ]; then
if [ "${STUNDE}" = "${HH24}" ] || [ "${STUNDE}" = "20" ] || { [ "${STUNDE}" = "" ] && [ "${HH24}" = "20" ]; }; then
# Insert the code to execute when the conditions are satisfied
fi
fi
As others have noted, your boolean expression can be simplified applying the law of distributivity of conjunction (⋀, *, AND) over disjunction (⋁, +, OR):
(a ⋀ b) ⋁ (a ⋀ c) = a ⋀ (b ⋁ c)
But to simplify it further, note you can, in bash, use && and || inside the (bash-specific) [[ .. ]] command:
[[ $a == 1 && $b == 2 ]]
Also, when using [[ .. ]] compound command (over POSIX [ .. ]) you don't have to quote variables. And to test for null-strings, you can use the shorter -z $var form over $var == "".
All this together yields:
if [[ $DATUM == $today ]] && [[ $STUNDE == $HH24 || $STUNDE == 20 || -z $STUNDE && $HH24 == 20 ]]; then
# ...
fi
To further simplify it, we would need to have more details on your application logic, possible values, etc.

Bash programming: evaluation in conditional statements

In bash scripting the if condition statement is not working properly with using "&&"
ARGCOUNT=$#
if (( "$ARGCOUNT" != "2" )) ;then
echo "number of arguments must be two"
fi
DFLAG=$1
HFLAG=$2
if (((( $DFLAG = "Mon" )) || (( $DFLAG = "MON" )) || (( $DFLAG = "mon" ))) && ((( HFLAG = "2" )) || (( HFLAG = "3" )) || (( HFLAG = "4" ))));then
echo " CS599 "
cd CS599
elif (((( $DFLAG = "Wed" )) || (( $DFLAG = "WED" )) || (( $DFLAG = "wed" ))) && ((( HFLAG = "2" )) || (( HFLAG = "3" )) || (( HFLAG = "4" ))));then
cd CS699
echo " CS699 "
elif (((( $DFLAG = "Fri" )) || (( $DFLAG = "FRI" )) || (( $DFLAG = "fri" ))) && ((( HFLAG = "2" )) || (( HFLAG = "3" )) || (( HFLAG = "4" ))));then
cd CS799
echo " CS799 "
else
echo "."
fi
my program is executing only else statement irrespective of arguments. means it evaluating if block false.
What is the problem ?
The parenthesis you use are for arithmetic evaluation. I think you are over using them, and it makes your script complicated.
This snippet below does work:
#!/bin/bash
ARGCOUNT=$#
if [ "$ARGCOUNT" -ne 2 ] ;then echo "number of arguments must be two"; fi
# put DFLAG in lower case (see man bash).
DFLAG=${1,,}
HFLAG=$2
if [ "$DFLAG" = 'mon' -a "$HFLAG" -ge 2 -a "$HFLAG" -le 4 ]; then
echo ok
else
echo failed
fi
As you can see, I optimized your expression:
Except for the case of $ARGCOUNT (which is safe because you initialized it to $#), don't forget to encase variable with double quote to avoid expansion.
In the declaration of DFLAG, I used the convert to lower case string operator (?). With that you won't have to check for each permutation of case in DFLAG. This might not work in bash3.
If you use the test or [ builtin, you can use -a between each expression to do a and.
Arithmetic evaluation with the test/[ builtin use the following operators: -ne (inequality), -eq (equality)-ge (greater or equal), -le (lesser or equals), -lt (lesser), -gt (greater).
As said in another answer, you can replace "$DFLAG" = 'mon' by "$DFLAG" == 'mon'. But this is not POSIX conformant (as said in my comment below) and I'm not enough knowledgeable on that to know if it's a good idea or not.
On a side note, if $HFLAG condition should always be the same, you can write your code like this:
if [ "$HFLAG" -ge 2 -a "$HFLAG" -le 4 ]; then
case "$DFLAG" in
mon|Mon|MON)
echo "monday";
;;
fry|Fry|FRY)
echo "friday";
;;
*)
echo "other"
;;
esac
fi
If that case, I putted back all permutation of case in case you were in bash3, to show you an example to do without ${DFLAG,}.
If you are looking for what mistake you did which is making condition to go to else part ...then it's simple mistake which almost every programmer do once in life ... using single "=" instead of "==" during comparison. Modify it accordingly and you should get expected flow/result in your script.
One eg.
$DFLAG = "Mon"
change to below notice the double equal sign
"$DFLAG" == "Mon"
First, you should go easy on the parenthesis. Those are fragile things.
Using Bash syntax (non-POSIX, less portable), you can write:
ARGCOUNT=$#
DFLAG=${1,,} # lower case
HFLAG=$2
# don't need the quotes in (( )) as we test arithmetic value
(( $ARGCOUNT != 2 )) && echo "number of arguments must be two"
shopt -s extglob # for #(1|2|3) below, see http://mywiki.wooledge.org/glob#extglob
if [[ $DFLAG = "mon" && $HFLAG == #(2|3|4) ]]; then
echo " CS599 "
cd CS599
if [[ $DFLAG = "wed" && $HFLAG == #(2|3|4) ]]; then
cd CS699
echo " CS699 "
if [[ $DFLAG = "fri" && $HFLAG == #(2|3|4) ]]; then
cd CS799
echo " CS799 "
else
echo "."
fi
Now you see how much you repeat yourself and can improve the algorithm. For instance, test HFLAG, if valid test DFLAG otherwise …
Read
Tests And Conditionals
Arithmetic Expression
Globbing extglob

OR logical operator in bash

I have very dumb problem but can't wrap my head around it
if [[ false || false ]] ; then
echo 'true'
else
echo 'false'
fi
As per http://tldp.org/LDP/abs/html/comparison-ops.html
-o logical or
exp1 -o exp2 returns true if either exp1 or exp2 is true.
These are similar to the Bash comparison operators && and ||, used
within double brackets. [[ condition1 && condition2 ]]
so if both are false then it should return false? then why it prints 'true'?
You should run those not as part of the conditional command '[[ ]]':
if false || false; then
echo 'true'
else
echo 'false'
fi
As for testing falses within [[ and ]]:
if [[ ! 1 || ! 1 ]]; then
echo 'true'
else
echo 'false'
fi
Noting that [[ false ]] is equivalent to [[ -n false ]] which makes a true condition.
If you like you could make a more apparent and valid conditional test with (( )) like this:
if (( 0 || 0 )); then
echo 'true'
else
echo 'false'
fi
"false" is not false. "false" is a non-empty string. Non-empty strings are true by default in [[.

Resources