问题
The words "true" and "false" are special words (builtins) for bash.
If used in an if
test, 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, this two tests:
$ [[ true ]] && echo "true" || echo "false"
true
$ [[ false ]] && echo "true" || echo "false"
true
Both result in true. Why?
回答1:
[[ … ]]
is, in this case, equivalent to test
, i.e. test true
and test false
. Looking at the manual for test(1):
-n STRING
the length of STRING is nonzero
STRING
equivalent to-n STRING
true
and false
are both non-empty strings.
回答2:
This is because in your first example, true
is a builtin command.
In your second exmaple, however, the true
inside [[ true ]]
is not interpreted as a command, but just treated as a string-like token, and it returns true if string is not empty.
The second example could be written like this for fix:
$ true && echo "true" || echo "false"
true
$ false && echo "true" || echo "false"
false
回答3:
When we use test or its equivalent [
we, sometimes, get strange results. Lets try to understand why that happens.
We can make simple tests manually:
$ 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 intermediately shocked that both the above tests are reported as true. Is test telling us that 0 is equal to 1 ?
To answer this question we could create a testing function and run all the tests that appear in this page.
A page which also aims to explain all the details with "test". With the aid of the testing function, we could run an script similar to the next one. It prints both the result of the test and the exit value of the function 'test':
#!/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"
The 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 tests above, it becomes clear the rule for unary test:
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's lenght is not zero, it's result is TRUE.
Well, that is the whole true for any "unary" test.
i.e. Tests perform on values that could NOT be split with spaces.
Tests that are binary [ -n $var ]
or ternary [ 1 -eq "$one" ]
result in a true value if the conditions (-n
or -eq
) are valid.
Amazingly, this (more complex) binary or ternary tests are more intuitive to understand. There exist a huge list of longer tests, but I feel that those fall outside the scope of this short question.
It is sometimes called pedantic to ask if a var is NOT zero length with the condition -n
as exactly the same test happens without any explicit condition.
However, it is often clearer to the reader what is the goal of the programer when the condition -n
is explicitly used.
The condition -z
tests for "zero length".
With this 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 test? Thanks.
来源:https://stackoverflow.com/questions/30229547/why-are-both-true-and-false-tests-true