Why are true and false tests correct?
The words "true" and "false" are special words (built-in) for bash. When used in a test if
, they act as intuitively expected:
$ if true; then echo "true"; else echo "false"; fi
true
$ if false; then echo "true"; else echo "false"; fi
false
However, these two tests:
$ [[ true ]] && echo "true" || echo "false"
true
$ [[ false ]] && echo "true" || echo "false"
true
Both results are true. Why?
This is because in your first example there true
is a builtin command.
However, the second example does not true
internally [[ true ]]
interpret it as a command, but simply treats it as a string token and returns true if the string is not empty.
The second example can be written like this: to fix:
$ true && echo "true" || echo "false"
true
$ false && echo "true" || echo "false"
false
source to share
When we use a test or its equivalent [
, we sometimes get strange results. Let's try to understand why this is happening.
We can do simple tests by hand:
$ test 0 && echo "0 is T|$?" || echo "0 is F|$?"
0 is T|0
$ test 1 && echo "1 is T|$?" || echo "1 is F|$?"
1 is T|0
or
$ [ 0 ] && echo "0 is T|$?" || echo "0 is F|$?"
0 is T|0
$ [ 1 ] && echo "1 is T|$?" || echo "1 is F|$?"
0 is T|0
And be shocked that both of the above tests are reported as true. Does the test show that 0 is 1?
To answer this question, we could create a test function and run all tests that appear on this page .
A page that also aims to explain all the details with a "test". With the test function, we could run a script similar to the following. It prints both the test result and the output value of the "test" function:
#!/bin/bash --
tval(){
printf "test %s\t" "$1"; shift
[ "$@" ] && printf "%15s is T|%s" "$*" "$?" || printf "%15s is F|%s" "$*" "$?"
printf "\n"
}
tval "zero" "0"
tval "one" "1"
tval "minus 1" "-1"
tval "string" "xyz"
tval "false" "false"
tval "true" "true"
tval "empty" ""
tval "Null" ""
tval "null var" "$xyz"
tval "\$false" "$false"
tval "\$true" "$true"
Results:
test zero 0 is T|0
test one 1 is T|0
test minus 1 -1 is T|0
test string xyz is T|0
test false false is T|0
test true true is T|0
test empty is F|1
test Null is F|1
test null var is F|1
test $false is F|1
test $true is F|1
From all the above tests, the rule for a unary test becomes clear:
The test command has a very simple mind. It always works is a very simple way.
Any time the tested value has some content, that is, it lenght is not zero, it result is TRUE.
Well, this is all true for any "unary" test. that is, tests execute values that cannot be separated by spaces. Tests that are binary [ -n $var ]
or ternary [ 1 -eq "$one" ]
result in a true value if the conditions ( -n
or -eq
) apply .
Surprisingly, these (more complex) binary or ternary tests are more intuitive. There is a huge list of longer tests out there, but I feel they are outside the scope of this short question.
It is sometimes called pedantic to ask if var is not null-length with a condition -n
, since the exact same test happens without any explicit condition.
However, it often becomes clearer to the reader what the programmer's goal is when the condition is explicitly used -n
.
The condition -z
checks for "zero length".
With these tests:
xyz="str"; tval "-n var" "-n" "$xyz"
xyz="str"; tval "-z var" "-z" "$xyz"
one=1; tval "1 -eq \$one" "1" "-eq" "$one"
one=2; tval "1 -eq \$one" "1" "-eq" "$one"
We get:
test -n var -n str is T|0
test -z var -z str is F|1
test 1 -eq $one 1 -eq 1 is T|0
test 1 -eq $one 1 -eq 2 is F|1
Any missing aspect of the test? Thank.