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

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.

This question is related to bash boolean conditional-operator

The answer is


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


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.


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

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

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.


Examples related to bash

Comparing a variable with a string python not working when redirecting from bash script Zipping a file in bash fails How do I prevent Conda from activating the base environment by default? Get first line of a shell command's output Fixing a systemd service 203/EXEC failure (no such file or directory) /bin/sh: apt-get: not found VSCode Change Default Terminal Run bash command on jenkins pipeline How to check if the docker engine and a docker container are running? How to switch Python versions in Terminal?

Examples related to boolean

Convert string to boolean in C# In c, in bool, true == 1 and false == 0? Syntax for an If statement using a boolean Truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all() Ruby: How to convert a string to boolean Casting int to bool in C/C++ Radio Buttons ng-checked with ng-model How to compare Boolean? Convert True/False value read from file to boolean Logical operators for boolean indexing in Pandas

Examples related to conditional-operator

Ternary operator in PowerShell Javascript one line If...else...else if statement How to do one-liner if else statement? What is the idiomatic Go equivalent of C's ternary operator? bash "if [ false ];" returns true instead of false -- why? One-line list comprehension: if-else variants Kotlin Ternary Conditional Operator Conditional statement in a one line lambda function in python? ORACLE IIF Statement Twig ternary operator, Shorthand if-then-else