bash "if [ false ];" returns true instead of false -- why?

BashBooleanConditional Operator

Bash Problem Overview


Why does the following output True?

#!/bin/sh

if [ false ]; then
	echo "True"
else
	echo "False"
fi

This will always output True even though the condition would seem to indicate otherwise. If I remove the brackets [] then it works, but I do not understand why.

Bash Solutions


Solution 1 - Bash

You are running the [ (aka test) command with the argument "false", not running the command false. Since "false" is a non-empty string, the test command always succeeds. To actually run the command, drop the [ command.

if false; then
   echo "True"
else
   echo "False"
fi

Solution 2 - Bash

A Quick Boolean Primer for Bash

The if statement takes a command as an argument (as do &&, ||, etc.). The integer result code of the command is interpreted as a boolean (0/null=true, 1/else=false).

The test statement takes operators and operands as arguments and returns a result code in the same format as if. An alias of the test statement is [, which is often used with if to perform more complex comparisons.

The true and false statements do nothing and return a result code (0 and 1, respectively). So they can be used as boolean literals in Bash. But if you put the statements in a place where they're interpreted as strings, you'll run into issues. In your case:

if [ foo ]; then ... # "if the string 'foo' is non-empty, return true"
if foo; then ...     # "if the command foo succeeds, return true"

So:

if [ true  ] ; then echo "This text will always appear." ; fi;
if [ false ] ; then echo "This text will always appear." ; fi;
if true      ; then echo "This text will always appear." ; fi;
if false     ; then echo "This text will never appear."  ; fi;

This is similar to doing something like echo '$foo' vs. echo "$foo".

When using the test statement, the result depends on the operators used.

if [ "$foo" = "$bar" ]   # true if the string values of $foo and $bar are equal
if [ "$foo" -eq "$bar" ] # true if the integer values of $foo and $bar are equal
if [ -f "$foo" ]         # true if $foo is a file that exists (by path)
if [ "$foo" ]            # true if $foo evaluates to a non-empty string
if foo                   # true if foo, as a command/subroutine,
                         # evaluates to true/success (returns 0 or null)

In short, if you just want to test something as pass/fail (aka "true"/"false"), then pass a command to your if or && etc. statement, without brackets. For complex comparisons, use brackets with the proper operators.

And yes, I'm aware there's no such thing as a native boolean type in Bash, and that if and [ and true are technically "commands" and not "statements"; this is just a very basic, functional explanation.

Solution 3 - Bash

I found that I can do some basic logic by running something like:

A=true
B=true
if ($A && $B); then
    C=true
else
    C=false
fi
echo $C

Solution 4 - Bash

Using true/false removes some bracket clutter...

#! /bin/bash    
#  true_or_false.bash

[ "$(basename $0)" == "bash" ] && sourced=true || sourced=false

$sourced && echo "SOURCED"
$sourced || echo "CALLED"

# Just an alternate way:
! $sourced  &&  echo "CALLED " ||  echo "SOURCED"

$sourced && return || exit

Solution 5 - Bash

as noted by @tripleee, this is tangential, at best.

still, in case you arrived here searching for something like that (as i did), here is my solution

having to deal with user acessible configuration files, i use this function :

function isTrue() {
    if [[ "${@^^}" =~ ^(TRUE|OUI|Y|O$|ON$|[1-9]) ]]; then return 0;fi
    return 1
    }

wich can be used like that

if isTrue "$whatever"; then..

You can alter the "truth list" in the regexp, the one in this sample is french compatible and considers strings like "Yeah, yup, on,1, Oui,y,true to be "True".

note that the '^^' provides case insensivity

Solution 6 - Bash

Adding context to hopefully help provide a bit of additional clarity on this subject. To a BaSH newbie, it's sense of true/false statements is rather odd. Take the following simple examples and their results.

This statement will return "true":

foo=" "; if [ "$foo" ]; then echo "true"; else echo "false"; fi

But this will return "false":

foo=" "; if [ $foo ]; then echo "true"; else echo "false"; fi

Do you see why? The first example has a quoted "" string. This causes BaSH to treat it literally. So, in a literal sense, a space is not null. While in a non-literal sense (the 2nd example above), a space is viewed by BaSH (as a value in $foo) as 'nothing' and therefore it equates to null (interpreted here as 'false').

These statements will all return a text string of "false":

foo=; if [ $foo ]; then echo "true"; else echo "false"; fi
foo=; if [ "$foo" ]; then echo "true"; else echo "false"; fi
foo=""; if [ $foo ]; then echo "true"; else echo "false"; fi
foo=""; if [ "$foo" ]; then echo "true"; else echo "false"; fi

Interestingly, this type of conditional will always return true:

These statements will all return a result of "true":

foo=""; if [ foo ]; then echo "true"; else echo "false"; fi

Notice the difference; the $ symbol has been omitted from preceding the variable name in the conditional. It doesn't matter what word you insert between the brackets. BaSH will always see this statement as true, even if you use a word that has never been associated with a variable in the same shell before.

if [ sooperduper ]; then echo "true"; else echo "false"; fi

Likewise, defining it as an undeclared variable ensures it will always return false:

if [ $sooperduper ]; then echo "true"; else echo "false"; fi

As to BaSH it's the same as writing:

sooperduper="";if [ $sooperduper ]; then echo "true"; else echo "false"; fi

One more tip....

Brackets vs No Brackets

Making matters more confusing, these variations on the IF/THEN conditional both work, but return opposite results.

These return false:

if [ $foo ]; then echo "true"; else echo "false"; fi
if [ ! foo ]; then echo "true"; else echo "false"; fi

However, these will return a result of true:

if $foo; then echo "true"; else echo "false"; fi
if [ foo ]; then echo "true"; else echo "false"; fi
if [ ! $foo ]; then echo "true"; else echo "false"; fi

And, of course this returns a syntax error (along with a result of 'false'):

if foo; then echo "true"; else echo "false"; fi

Confused yet? It can be quite challenging to keep it straight in your head in the beginning, especially if you're used to other, higher level programming languages.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontenmilesView Question on Stackoverflow
Solution 1 - BashchepnerView Answer on Stackoverflow
Solution 2 - BashBeejorView Answer on Stackoverflow
Solution 3 - BashRodrigoView Answer on Stackoverflow
Solution 4 - BashpshView Answer on Stackoverflow
Solution 5 - BashEric YriarteView Answer on Stackoverflow
Solution 6 - BashMrPotatoHeadView Answer on Stackoverflow