Multiple conditions on bash 'if' statement - bash

i'm having trouble with if in bash.
basically i'm trying to do this in bash:
if ((x='r')&&(y='s'))||((x='s')&&(y='p')) then
echo "bluh"
but i've just can't find the proper way, used square brackets, 2 square brackets, round brackets.
but it just won't work...

(( )) are for bash arithmetic, instead you need [[ ]] bash test :
if [[ ( $x == r && $y == s ) || ( $x == s && $y == p ) ]]; then
echo "bluh"
fi
[[ is a bash keyword similar to (but more powerful than) the [ command. See http://mywiki.wooledge.org/BashFAQ/031 and http://mywiki.wooledge.org/BashGuide/TestsAndConditionals. Unless you're writing for POSIX sh, we recommend [[.

Related

Multiple if condition in bash not working

I have written the following bash script:
if [ "crack" == "crack" -a "something/play" == *"play"* ];
then
echo "Passed"
else
echo "Failed"
fi
However the right side of this comparison is not working.
I noticed that if I use it the only right side with [[ "something/play" == *"play"* ]] it works correctly but how do I combine the two conditions inside the if clause.
It's a difference between [ and [[. The first is a standard command, where = just tests for equality. (Note that the standard operator is =, not ==.) The latter is a feature from ksh, supported in Bash and Zsh, and there, =/== is a pattern match. Also, you should avoid using -a within [ .. ], it can break if you do something like [ "$a" = foo -a "$b" = bar ] and $a or $b contains a !.
So,
$ if [[ "crack" == "crack" && "something/play" == *"play"* ]]; then echo true; fi
true
See also (in unix.SE): Why is [ a shell builtin and [[ a shell keyword? and What is the difference between the Bash operators [[ vs [ vs ( vs ((?.
If you use double brackets you can chain conditions with && (and) and || (or).
if [[ "crack" == "crack" && "something/play" == *"play"* ]]
then
echo "Passed"
else
echo "Failed"
fi

How to write a one liner test containing "||"s and "&&"s [duplicate]

I would like to recreate something like this
if ( arg1 || arg2 || arg 3) {}
And I did got so far, but I get the following error:
line 11: [.: command not found
if [ $char == $';' -o $char == $'\\' -o $char == $'\'' ]
then ...
I tried different ways, but none seemed to work. Some of the ones I tried.
For Bash, you can use the [[ ]] form rather than [ ], which allows && and || internally:
if [[ foo || bar || baz ]] ; then
...
fi
Otherwise, you can use the usual Boolean logic operators externally:
[ foo ] || [ bar ] || [ baz ]
...or use operators specific to the test command (though modern versions of the POSIX specification describe this XSI extension as deprecated -- see the APPLICATION USAGE section):
[ foo -o bar -o baz ]
...which is a differently written form of the following, which is similarly deprecated:
test foo -o bar -o baz
Bash's [[ ]] and (( )) are more powerful and flexible than [ ].
[[ ]] is for strings and (( )) is for integer logic and arithmetic
&& and || operators can be used inside [[ ]] and (( )), and () can be used for grouping
No need to quote variable expansions inside [[ ]] or (( )) - Bash doesn't do word splitting or globbing in these contexts
Inside (( )), there is no need for a $ behind variable names to expand them
[[ ]] and (( )) can span multiple lines, without the need for a line continuation with \
Using these, we can write clean, readable, and more reliable code.
Examples
Compound statements with integers
a=1 b=2 c=3
((a == 2 || (b == 2 && c == 3))) && echo yes # yields yes
Compound statements with strings
x=apple y=mango z=pear
[[ $x == orange || ($y == mango && $z == pear) ]] && echo yes # yields yes
[ equivalents for the above statements, using {}
[ "$a" -eq 2 ] || { [ "$b" -eq 2 ] && [ "$c" -eq 3 ]; }
[ "$x" == orange ] || { [ $y == mango ] && [ "$z" == pear ]; }
Related
Is double square brackets [[ ]] preferable over single square brackets [ ] in Bash?
How to use double or single brackets, parentheses, curly braces
Comparing integers: arithmetic expression or conditional expression on Unix & Linux
Test for non-zero length string in Bash: [ -n “$var” ] or [ “$var” ]
Conditional Expressions - Bash Manual
Charles' answer is correct in that it shows you how to do logical operations on commands within (and without, for that matter) an if statement, but it looks more like you want to use case here:
case $char in
\;|\\|\') echo found;;
*) echo 'not found';;
esac
In bash, to group conditionals using the [ ] construct you must surround each group using parenthesis. Each opening/closing parenthesis must be escaped and preceded/succeeded with a white space. See below:
if [ \( "$char" = "$;" \) -o \( "$char" = "$\\" \) -o \( "$char" = "$\" \) ]
As such, it's definitely best to follow everyone elses advice and use bash's newer [[ ]] construct. Lastly, as I understand it == is a relational operator intended to be used with-in arithmetic expressions. i.e. -
$((3==4))
Cameron Newham and Bill Rosenblatt, "Learning the bash Shell" Jan.1998

Why does this if statement get evaluated to true in bash?

# prints "here" when run in bash
if [[ ((9 > 220)) ]]; then echo "here"; fi
I'm confused why the above if statement gets evaluated to true. Wouldn't ((9 > 220)) evaluate to false which would make the if statement false?
The code below behaves as expected. I'm confused why using the double parentheses in a double brackets "if" isn't working above though.
# doesn't print anything
if ((9 > 220)); then echo "here"; fi
There's a fundamental difference between those two compound commands inside a conditional construct.
[[ expression ]] compound command
In your first example you're using the [[ expression ]] command:
if [[ ((9 > 220)) ]]; then echo "here"; fi
where parenthesis are treated merely as grouping operators, used to override the normal precedence of other operators (like !, &&, ||, >, -gt, -e, etc.). The > operator in this case is a lexicographic greater than.
This is nicely described in man bash:
[[ expression ]]
Return a status of 0 or 1 depending on the evaluation of the conditional expression expression. Expressions are composed of the primaries described below under CONDITIONAL EXPRESSIONS. Word splitting and pathname expansion
are not performed on the words between the [[ and ]]; tilde expansion, parameter and variable expansion, arithmetic expansion, command substitution, process substitution, and quote removal are performed. Conditional operators such as -f must be unquoted to be recognized as primaries.
When used with [[, the < and > operators sort lexicographically using the current locale.
So, to compare integers in the [[ compound command, you can use the conditional expression operators, the same one used by test and [ commands. For example like this:
if [[ 9 -gt 220 ]]; then echo "here"; fi
The result is the same like when the -gt operator is grouped with parenthesis:
if [[ ((9 -gt 220)) ]]; then echo "here"; fi
Alternatively, you can use the arithmetic expansion and exploit the fact that the boolean results are represented as "0" or "1":
if [[ $((9 > 200)) == 1 ]]; then echo "here"; fi
(( expression )) compound command
In your second example, you're using the (( expression )) command:
if ((9 > 220)); then echo "here"; fi
where the expression is evaluated according to the rules of the shell arithmetic. The man bash says:
((expression))
The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent
to let "expression".
It is because using double brackets makes it a lexicographical comparison. That means that it checks for which sequence is longer, like sorting alphabetically. More information about lexicographical comparisons here
Inside of [[ ]], the parentheses don't trigger an arithmetic context, they're interpreted purely for grouping/precedence1. All of these are equivalent:
$ [[ 0 < 1 ]] && echo yes
yes
$ [[ (0 < 1) ]] && echo yes
yes
$ [[ ((0 < 1)) ]] && echo yes
yes
$ [[ (((0 < 1))) ]] && echo yes
yes
$ [[ ((((0 < 1)))) ]] && echo yes
yes
If we have unbalanced parentheses, Bash complains about that:
$ [[ ((((0 < 1))) ]] && echo yes
bash: unexpected token `]]', expected `)'
bash: syntax error near `]]'
This all being said, < and > within [[ ]] are for lexicographical string comparison, so the statement above just checks if 0 is lexicographically sorted before 1 (it is).
Observe:
$ [[ 11 < 2 ]] && echo yes
yes
To compare numbers, you have to use -gt, -lt, -ge, -le, eq, ne instead of >, <, >=, <=, =, !=:
$ [[ 11 -lt 2 ]] && echo yes || echo no
no
Since you're already using (( )), you could use just that for comparing numbers:
$ (( 11 < 2 )) && echo yes || echo no
no
This is the simplest and clearest method in my opinion, if you know you have Bash at your disposal.
1 See the manual about [[ ]]:
Expressions may be combined using the following operators, listed in
decreasing order of precedence:
( expression )
Returns the value of expression. This may be used to override the normal precedence of operators.
Hat tip to glenn jackman for pointing to the manual in his comment to another answer.
The double parentheses is an arithmetic context. If you use the brackets, you have to use -lt for less than and -gt for greater than. See Comparing numbers in Bash.

How to use arithmetic in an if statement for Bash

Hi Guys I am trying to perform a check in my bash script that needs to meet three conditions, I was able to make the first 2 conditions work in the if statement as I wanted, but when I wanted to implement a third check with some arithmetic using the && operator, the script does not even launch.
DOUBLE_CHECK=0
if [[ -z "$avail" && "$WIFI_ID" == "some_str" && 'expr $DOUBLE_CHECK % 2' -eq "0"]]; then
sudo caffeinate xterm -geometry 70x20+0+0 -fa monospace -fs 8 -e './script1.sh' & disown
fi
(($DOUBLE_CHECK++))
The Idea is I want the third check to have a number that increments over time inside my while loop and then checked, whenever it is divisible by 2 it passes the 3rd condition of the if statement
As noted in a separate answer, you could use backticks or preferably $() to expand the expression to the output of the inner command, like this :
if [[ -z "$avail" && $WIFI_ID == "some_str" && $(expr $DOUBLE_CHECK % 2) -eq "0" ]]
Please note I have added a (required) space before the final ]].
Another possibility is to use an arithmetic expression :
if [[ -z $avail && $WIFI_ID == "some_str" ]] && ((DOUBLE_CHECK % 2 == 0))
Please note I have removed harmless but unnecessary quotes : the double-bracketed conditional expression is not a normal command, it is special shell syntax, and it does not perform word splitting. I have left quotes around some_str, because equality/inequality comparisons will perform pattern matching on the right-hand expression if it is not quoted. There is also no $ before the variable name in the arithmetic expression : it works, but is not required inside (()) or $(()).
The expression could also be expressed as :
if [[ -z $avail && $WIFI_ID == "some_str" ]] && ! ((DOUBLE_CHECK % 2))
The reason for this is that (( )) has a return code of 0 if its numerical result is non-zero, and a non-zero return code otherwise.
There are a couple small issues
Your third condition should be evaluated using back-ticks ('`'), instead of quotes. Single-quotes in bash mean string literal. So, 'expr $DOUBLE_CHECK %2' -eq "0" will always be false
The check -eq is used to compare numbers. Your statement is using strings (to compare strings, use the normal == syntax)
Please try,
DOUBLE_CHECK=0
if [[ -z "$avail" ]] && [[ "$WIFI_ID" == "some_str" ]] && [[ `expr $DOUBLE_CHECK % 2` -eq 0 ]]; then
sudo caffeinate xterm -geometry 70x20+0+0 -fa monospace -fs 8 -e './script1.sh' & disown
fi
(($DOUBLE_CHECK++))
You can use [[ .. ]] for string logic (where = and == are equivalent) and (( ... )) for numeric comparison, and do away with expr:
if [[ -z "$avail" && "$WIFI_ID" = "some_str" ]] && (( DOUBLE_CHECK % 2 == 0 )); then
# your logic here
fi
Note that prefixing variables with $ is optional inside (( ... )) and quoting variables is optional inside [[ ... ]] since doesn't do word splitting and globbing there.

Why doesn't my comparison in BASH work?

I am trying to compare some characters in BASH
read a
if (($a == "Y"))
then
echo "YES"
elif (($a == "y"))
then
echo "YES"
else
echo "NO"
fi
but for some reason it does not work as expected. It always output YES. Looking at this super simple script, I can not understand what is wrong.
It's due to spacing and the brackets.
read a
if [[ $a == "Y" ]]
then
echo "YES"
elif [[ $a == "y" ]]
then
echo "YES"
else
echo "NO"
fi
You should review bash comparison. You're trying to use an arithmetic expansion construct to do string comparison. Instead, you should use the [ exp ] or [[ exp ]] constructs.
((expression)) is used for 'arithmetic evaluation' and strings inside (( )) will be treated as variable names, thus
if (($a == "Y"))
is equivalent to
if [ $a == $Y ]
(if $Y is a string, then bash will try to expand the name until it finds a numeric value or undefined variable)
You need to use [ ] or [[ ]] to compare strings as #KRUKUSA said.

Resources