0

Why does it work if I test a single variable if it's true, but it doesn't work if I check 2 variables if either is true? Am I overlooking something super obvious and simple here?

Why does below code work:

var1=false
var2=false

if "$var1"; then
  echo "true"
else
  echo "false"
fi

but this doesn't:

var1=false
var2=false

if [[ "$var1" || "$var2" ]]; then
    echo "true"
else
    echo "false: "
fi

as it always returns true

Joost
  • 82
  • 1
  • 9
  • Try `if [[ "$var1" == true || "$var2" == true ]]; then`. – DaBler Aug 17 '23 at 16:36
  • The equivalent for two variables is `if "$var1" || "$var2"`, no `[[ ... ]]` involved. – Benjamin W. Aug 17 '23 at 16:54
  • Also see [When are square brackets required in a Bash if statement?](https://stackoverflow.com/q/8934012/4154375). – pjh Aug 17 '23 at 19:44
  • `if false` returns the program named `false`, which sets exit code 1. `if [[ false ]]` tests whether the string between the brackets is not empty, and if this is the case (and it is, because the word _false_ consists of 5 characters), sets exit code 0. In simpler words: You have program execution in the first case, and string test in the second. You can't expect the same result .... – user1934428 Aug 18 '23 at 07:05

3 Answers3

3

I you have

var=true
if "$var"; then

you're actually running

if true; then

The variable expands, and the true command is executed.

If you do

var1=false
if [[ $var ]]; then

you're running a shortcut for

if [[ -n $var ]]

which is true if $var is not empty.

So, this

[[ $var1 || $var2 ]]

is true if any of $var1 or $var2 are not empty, no matter what they contain.

To check if any of them has the value true, don't use [[ ... ]] at all:

if "$var1" || "$var2"

Notice that Bash doesn't have the concept of booleans. true and false are just strings, and if "$var1" behaves the way it does because there is a command called true (and also one for false).

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
  • 1
    When `var1=date`, you will be executing something you don't want. – Walter A Aug 17 '23 at 19:19
  • @WalterA I think it's generally not a good idea to rely on the exit status of commands stored in variables. I suspect people use this because `if true` and `if false` look like more conventionally used boolean logic, but for Bash, there are likely better approaches. – Benjamin W. Aug 17 '23 at 19:23
1

The comparison isn't doing whatever it is you think it's doing, and the two statements are entirely dissimilar. Because of the way Bash parses such things:

set -x

var1="false"
if "$var1"; then
    echo "This branch never executes."
fi

var1="true"
if "$var1"; then echo $?; fi
set +x

shows that this minor adaptation of your code is actually running the true or false binary in your PATH (probably /bin/false or /bin/true in most cases) and returning the exit status from the binary. Likewise, [[ "true" ]] and [[ "false" ]] always exit zero because this construct creates in implicit -n test (e.g. variable exists and is not empty), so you aren't doing any comparisons where there's a failing condition since both var1 and var2 are defined.

What you probably want is string equality. For example:

set -x

var1="false"
var2="true"

if [[ "$var1" = "true" ]] || [[ "$var2" = "true" ]]
then
  echo "At least one of the two values is true."
fi

set +x

This performs a string comparison between the values, and returns an appropriate exit status with a Boolean short-circuit.

Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
0

The reason is that they are testing different things.

if [[ "$VAR1" ]]
then 
    echo "true"
else
    echo "false" 
fi

And anything else within a [[ ]] tests if the variable has more than 0 characters. This is like [[ -n "$VAR1" ]]. So a variable like VAR3="" would return false. But checking:

if "$VAR1"
then 
    echo "true"
else
    echo "false" 
fi

Test the content of the variable. If you will run the 1st check with an undefined variable (say, $VAR3, in this case) it would return false, whereas doing the same with the 2nd test would simply return an error.

Uberhumus
  • 921
  • 1
  • 13
  • 24
  • 5
    `[[ "$VAR1" ]]` doesn't test that a variable exists, it's equivalent to `[[ -n "$VAR1" ]]` which tests that the length of the string is non-zero. `if "$VAR1"` is running the contents of the variable as a command and branching based on its exit status. – tjm3772 Aug 17 '23 at 17:02
  • Yes, exactly. A variable existing would be tested with e.g. `[[ -v VAR1 ]]` (note no dollar sign) or with some parameter expansion trick like `[ "${VAR1+x}" = "x" ]` in older shells that don't support the bash-specific extended test. – tjm3772 Aug 17 '23 at 17:11
  • @tjm3772, corrected the answer. Thanks! – Uberhumus Aug 17 '23 at 17:49