Simple logical operators in Bash
What you've written actually almost works (it would work if all the variables were numbers), but it's not an idiomatic way at all.
-
(…)
parentheses indicate a subshell. What's inside them isn't an expression like in many other languages. It's a list of commands (just like outside parentheses). These commands are executed in a separate subprocess, so any redirection, assignment, etc. performed inside the parentheses has no effect outside the parentheses.- With a leading dollar sign,
$(…)
is a command substitution: there is a command inside the parentheses, and the output from the command is used as part of the command line (after extra expansions unless the substitution is between double quotes, but that's another story).
- With a leading dollar sign,
-
{ … }
braces are like parentheses in that they group commands, but they only influence parsing, not grouping. The programx=2; { x=4; }; echo $x
prints 4, whereasx=2; (x=4); echo $x
prints 2. (Also braces require spaces around them and a semicolon before closing, whereas parentheses don't. That's just a syntax quirk.)- With a leading dollar sign,
${VAR}
is a parameter expansion, expanding to the value of a variable, with possible extra transformations.
- With a leading dollar sign,
-
((…))
double parentheses surround an arithmetic instruction, that is, a computation on integers, with a syntax resembling other programming languages. This syntax is mostly used for assignments and in conditionals.- The same syntax is used in arithmetic expressions
$((…))
, which expand to the integer value of the expression.
- The same syntax is used in arithmetic expressions
-
[[ … ]]
double brackets surround conditional expressions. Conditional expressions are mostly built on operators such as-n $variable
to test if a variable is empty and-e $file
to test if a file exists. There are also string equality operators:"$string1" == "$string2"
(beware that the right-hand side is a pattern, e.g.[[ $foo == a* ]]
tests if$foo
starts witha
while[[ $foo == "a*" ]]
tests if$foo
is exactlya*
), and the familiar!
,&&
and||
operators for negation, conjunction and disjunction as well as parentheses for grouping. Note that you need a space around each operator (e.g.[[ "$x" == "$y" ]]
, not), and a space or a character like[[ "$x"=="$y" ]]
;
both inside and outside the brackets (e.g.[[ -n $foo ]]
, not).[[-n $foo]]
-
[ … ]
single brackets are an alternate form of conditional expressions with more quirks (but older and more portable). Don't write any for now; start worrying about them when you find scripts that contain them.
This is the idiomatic way to write your test in bash:
if [[ $varA == 1 && ($varB == "t1" || $varC == "t2") ]]; then
If you need portability to other shells, this would be the way (note the additional quoting and the separate sets of brackets around each individual test, and the use of the traditional =
operator rather than the ksh/bash/zsh ==
variant):
if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then
very close
if [[ $varA -eq 1 ]] && [[ $varB == 't1' || $varC == 't2' ]];
then
scale=0.05
fi
should work.
breaking it down
[[ $varA -eq 1 ]]
is an integer comparison where as
$varB == 't1'
is a string comparison. otherwise, I am just grouping the comparisons correctly.
Double square brackets delimit a Conditional Expression. And, I find the following to be a good reading on the subject: "(IBM) Demystify test, [, [[, ((, and if-then-else"
A very portable version (even to legacy bourne shell):
if [ "$varA" = 1 -a \( "$varB" = "t1" -o "$varB" = "t2" \) ]
then do-something
fi
This has the additional quality of running only one subprocess at most (which is the process [
), whatever the shell flavor.
Replace =
with -eq
if variables contain numeric values, e.g.
-
3 -eq 03
is true, but -
3 = 03
is false. (string comparison)