Operations on boolean variables Operations on boolean variables shell shell

Operations on boolean variables


This does work:

if ! $var1 && $var2; then   echo "do sth"fi

Maybe somebody can explain why -a and -o operators don't work and &&, ||, ! do?


Okay boys and girls, lesson time.

What's happening when you execute this line?

if true ; then echo 1 ; fi

What's happening here is that the if command is being executed. After that everything that happens is part of the if command.

What if does is it executes one or more commands (or rather, pipelines) and, if the return code from the last command executed was successful, it executes the commands after then until fi is reached. If the return code was not successful the then part is skipped and execution continues after fi.

if takes no switches, its behavior is not modifiable in anyway.

In the example above the command I told if to execute was true. true is not syntax or a keyword, it's just another command. Try executing it by itself:

true

It will print nothing, but it set its return code to 0 (aka "true"). You can more clearly see that it is a command by rewriting the above if statement like this:

if /bin/true ; then echo 1 ; fi

Which is entirely equivalent.

Always returning true from a test is not very useful. It is typical to use if in conjunction with the test command. test is sometimes symlinked to or otherwise known as [. On your system you probably have a /bin/[ program, but if you're using bash [ will be a builtin command. test is a more complex command than if and you can read all about it.

help [man [

But for now let us say that test performs some tests according to the options you supply and returns with either a successful return code (0) or an unsuccessful one. This allows us to say

if [ 1 -lt 2 ] ; then echo one is less than two ; fi

But again, this is always true, so it's not very useful. It would be more useful if 1 and 2 were variables

read -p' Enter first number: ' firstread -p' Enter second number: ' secondecho first: $firstecho second: $secondif [ $first -lt $second ] ; then     echo $first is less than $secondfi

Now you can see that test is doing its job. Here we are passing test four arguments. The second argument is -lt which is a switch telling test that the first argument and third argument should be tested to see if the first argument is less than the third argument. The fourth argument does nothing but mark the end of the command; when calling test as [ the final argument must always be ].

Before the above if statement is executed the variables are evaluated. Suppose that I had entered 20 for first and 25 for second, after evaluation the script will look like this:

read -p' Enter first number: ' firstread -p' Enter second number: ' secondecho first: 20echo second: 25if [ 20 -lt 25 ] ; then     echo 20 is less than 25fi

And now you can see that when test is executed it will be testing is 20 less than 25?, which is true, so if will execute the then statement.

Bringing it back to the question at hand: What's going on here?

var1=truevar2=falseif ! $var1 -a $var2 ; then    echo $var1 and $var2 are both truefi

When the if command is evaluated it will become

if ! true -a false ; then

This is instructing if to execute true and passing the arguments -a false to the true command. Now, true doesn't take any switches or arguments, but it also will not produce an error if you supply them without need. This means that it will execute, return success and the -a false part will be ignored. The ! will reverse the success in to a failure and the then part will not be executed.

If you were to replace the above with a version calling test it would still not work as desired:

var1=truevar2=falseif ! [ $var1 -a $var2 ] ; then    echo $var1 and $var2 are both truefi

Because the if line would be evaluated to

if ! [ true -a false ; ] then

And test would see true not as a boolean keyword, and not as a command, but as a string. Since a non-empty string is treated as "true" by test it will always return success to if, even if you had said

if ! [ false -a yourmom ] ; then

Since both are non-empty strings -a tests both as true, returns success which is reversed with ! and passed to if, which does not execute the then statement.

If you replace the test version with this version

if ! $var1 && $var2 ; then

Then it will be evaluated in to

if ! true && false ; then

And will be processed like this: if executes true which returns success; which is reversed by !; because the return code of the first command was failure the && statement short circuits and false never gets executed. Because the final command executed returned a failure, failure is passed back to if which does not execute the then clause.

I hope this is all clear.

It is perhaps worth pointing out that you can use constructs like this:

! false && true && echo 1

Which does not use if but still checks return codes, because that is what && and || are for.

There is kind of a black art to using test without making any mistakes. In general, when using bash, the newer [[ command should be used instead because it is more powerful and does away with lots of gotchas which must, for compatibility reasons, be kept in [.

Since the original poster did not supply a realistic example of what he's trying to accomplish it's hard to give any specific advice as to the best solution. Hopefully this has been sufficiently helpful that he can now figure out the correct thing to do.


You have mixed here two different syntaxes.

This will work:

if ! [ 1 -a 2 ]; then   echo "do sth"fi

Note brackets around the expressions.

You need the test command ([ in newer syntax) to use these keys (-a, -o and so on).

But test does nut run commands itself.If you want to check exit codes of commands you must not use test.