$ false
$ echo $?

$ if [[ false ]]; then echo 'do it'; fi
do it

$ test false && echo 'do it'
do it

$ COND=false
$ echo $COND
$ if [[ $COND -ne 0 ]]; then echo 'do it'; fi
$ if [[ $COND -ne true ]]; then echo 'do it'; fi

This is nonsense IMO, what am I missing here?

-- Update

So the following is how it goes:

$ COND=false
$ if ! $COND; then echo 'do it'; fi
do it
$ if $COND; then echo 'do it'; fi

But I want to combine $COND with other conditional expressions in one larger test expression (or [[ {expr} && {expr} ]]). So when $COND is false it's not working inside an [[ {expr} ]] in an intuitive manner. It seems the [[ {expr} ]] is a bit misleading when what I need is more like:

$ COND=false
$ if ! $COND && test -d ${HOME}/bin ; then echo 'do it'; fi
do it

Shell syntax is highly context-dependent. That is, what a particular thing (like true or false) means depends a great deal on where it occurs. In your examples, false has three completely different meanings: a command, a string value, and a (nonexistent) variable name containing an integer value. Let me run through the examples:

  • false as a command:

    false; echo $?    # prints "1"
    if false; then echo "yep"; else echo "nope"; fi    # prints "nope"

    There's a command named "false" (generally /usr/bin/false, or a bash builtin that does the same thing) that doesn't really do anything except exit with a failure status. As an exit status, zero indicates success (which is sort of truth-like) and nonzero indicates failure (which is sort of false-like). This is the opposite of the more usual zero=false, nonzero=true convention, but for exit statuses it makes more sense.

  • false as an uninterpreted string value:

    if [[ false ]]; then echo "yep"; else echo "nope"; fi     # prints "yep"
    if [[ true ]]; then echo "yep"; else echo "nope"; fi      # prints "yep"
    if [[ wibble ]]; then echo "yep"; else echo "nope"; fi    # prints "yep"
    if [[ "this is a string" ]]; then echo "yep"; else echo "nope"; fi    # prints "yep"
    [[ false ]]; echo $?    # prints "0" (=success)
    [ false ]; echo $?      # prints "0" (=success)
    test false; echo $?     # prints "0" (=success)

    In all these cases, the command test or its synonym [ or the bash conditional expression [[ ]] didn't get an expression, just a single string, so they perform a very simple test: is it a nonzero-length string? "true", "false", "wibble", etc are all nonzero-length, so the command/expression is truth-like, hence successful. Compare with:

    [[ "" ]]; echo $?    # prints "1" (=failure), because the string is zero-length
    [[ ]]; echo $?       # prints "1" (=failure), because there isn't even a string
    [ ]; echo $?         # same

    Note that test and [ are normal commands, and "false" (and "wibble" etc) is just an argument to it. [[ ]] is a bit of bash syntax that can be used in place of a command. Their arguments/contents are parsed differently from the command names themselves.

  • false as a possible variable name that might contain an integer:

    if [[ false -eq true ]]; then echo "equal"; else echo "nope"; fi     # prints "equal"

    This one's a bit weirder, and depends on the details of how bash's [[ ]] tests for numeric equality. Note that in [[ ]] (and [ ] and test expressions), -eq tests for numeric equality, and = tests for string equality. So for example, [[ 01 -eq 1 ]] (because 1 and 01 are numerically equal) is true, but [[ 01 = 1 ]] is false (because they're not the same string). In the case of [[ false -eq true ]], "true" and "false" are not integer values, so bash tries to convert them to integers by treating them as variable names (and hoping the variables contain integer values). In fact, neither is defined as a variable, so they both evaluate to the empty string, which can sort of be interpreted as the integer 0. Observe:

    if [[ false -eq true ]]; then echo "equal"; else echo "nope"; fi     # prints "equal"
    if [[ false -eq 0 ]]; then echo "equal"; else echo "nope"; fi        # prints "equal"
    if [[ false -eq true ]]; then echo "equal"; else echo "nope"; fi     # now prints "nope"

    Note that defining false as a variable has no effect on its other uses; when used as a command it'll still exit with status 1, and when used as a string it's still just "false" (not "5").


You're not running the false command, but using [[ (similar to test and [ builtin commands) to evaluate the expression "false", and without giving it anything else to evaluate it's doing this:

-n string
True if the length of string is non-zero.

See the CONDITIONAL EXPRESSIONS section of man bash for more info.

if does execute the command given, then examines it's exit status.

[Referenced courtesy the answer from SO]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.