15

I am attempting to run a block of code if one flag is set to true and the other is set to false. ie

var1=true
var2=false
if [[ $var1 && ! $var2 ]]; then var2="something"; fi

Since that did not evaluate the way that I expected I wrote several other test cases and I am having a hard time understanding how they are being evaluated.

aa=true 
bb=false
cc="python"
if [[ "$aa" ]]; then echo "Test0" ; fi
if [[ "$bb" ]]; then echo "Test0.1" ; fi
if [[ !"$aa" ]]; then echo "Test0.2" ; fi
if [[ ! "$aa" ]]; then echo "Test0.3" ; fi
if [[ "$aa" && ! "$bb" ]]; then echo "Test1" ; fi
if [[ "$aa" && ! "$aa" ]]; then echo "Test2" ; fi
if [[ "$aa" ]] && ! [[ "$bb" ]]; then echo "test3" ; fi
if [[ "$aa" ]] && ! [[ "$cc" ]]; then echo "test4" ; fi
if [[ $aa && ! $bb ]]; then echo "Test5" ; fi
if [[ $aa && ! $aa ]]; then echo "Test6" ; fi
if [[ $aa ]] && ! [[ $bb ]]; then echo "test7" ; fi
if [[ $aa ]] && ! [[ $cc ]]; then echo "test8" ; fi

When I run the preceding codeblock the only output I get is

Test0
Test0.1
Test0.2

however, my expectation is that I would get

Test0
Test1
Test3
Test5
Test7

I have tried to understand the best way to run similar tests, however most examples I have found are set up in the format of
if [[ "$aa" == true ]];
which is not quite what I want to do. So my question is what is the best way to make comparisons like this, and why do several of the test cases that I would expect to pass simply not?

Thank you!

JLMarks
  • 165
  • 1
  • 1
  • 6

5 Answers5

18

Without any operators, [[ only checks if the variable is empty. If it is, then it is considered false, otherwise it is considered true. The contents of the variables do not matter.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • Thank you. After I commented out the line `bb=false` and then reran the script I see how that works, it gave me output more in line with what I was expecting. – JLMarks Apr 27 '14 at 06:48
  • As an odd aside, this is the second question I have ever asked on stackoverflow, the first being [over two years ago](http://stackoverflow.com/questions/9170155/is-there-a-simple-to-split-and-rejoin-a-tarfile-using-python). In that question you where the only user to answer me. I just now got around to accepting your answer, so double thanks! – JLMarks Apr 27 '14 at 06:54
  • can you put a copy-pasteable answer here? the corrected code? thanks – Walrus the Cat Jan 24 '17 at 22:54
18

Your understanding of booleans in shell context is incorrect.

var1=true
var2=false

Both the above variables are true since those are non-empty strings.

You could instead make use of arithmetic context:

$ a=1
$ b=0
$ ((a==1 && b==0)) && echo y
y
$ ((a==0 && b==0)) && echo y
$
$ ((a && !(b))) && echo y;       # This seems to be analogous to what you were attempting
y
devnull
  • 118,548
  • 33
  • 236
  • 227
  • 2
    You may combine this with `readonly true=1 yes=1 no=0 false=0` to create reliable constants for `true` and `false` within arithmetic expressions. – Henk Langeveld Apr 27 '14 at 06:59
12

The shell does not have Boolean variables, per se. However, there are commands named true and false whose exit statuses are 0 and 1, respectively, and so can be used similarly to Boolean values.

var1=true
var2=false
if $var1 && ! $var2; then var2="something"; fi

The difference is that instead of testing if var1 is set to a true value, you expand it to the name of a command, which runs and succeeds. Likewise, var2 is expanded to a command name which runs and fails, but because it is prefixed with ! the exit status is inverted to indicate success.

(Note that unlike most programming languages, an exit status of 0 indicates success because while most commands have 1 way to succeed, there are many different ways they could fail, so different non-zero values can be assigned different meanings.)

chepner
  • 497,756
  • 71
  • 530
  • 681
8

true and false are evaluated as strings ;)

[[ $var ]] is an equivalent of [[ -n $var ]] that check if $var is empty or not.

Then, no need to quote your variables inside [[. See this reminder.

Finally, here is an explication of the difference between && inside brackets and outside.

Community
  • 1
  • 1
Idriss Neumann
  • 3,760
  • 2
  • 23
  • 32
0

The closest you can come seems to be use functions instead of variables because you can use their return status in conditionals.

$  var1() { return 0; }
$  var2() { return 1; }  # !0 = failure ~ false

and we can test this way

$  var1 && echo "it's true" || echo "it's false"
it's true
$  var2 && echo "it's true" || echo "it's false"
it's false

or this way

$  if var1; then echo "it's true"; else echo "it's false"; fi
it's true
$  if var2; then echo "it's true"; else echo "it's false"; fi
it's false

Hope this helps.

Brandonian
  • 61
  • 1
  • 1
  • See @chepner above: http://stackoverflow.com/a/23324368/1246494 true/false functions are built in to act as boolean values. – Bradmage Jun 11 '16 at 17:19