How does bash test 'false'?
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[ ]
andtest
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" false=5 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").