Why are both “true” and “false” tests true?

2019-03-31 07:15发布

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?

3条回答
孤傲高冷的网名
2楼-- · 2019-03-31 07:27

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楼-- · 2019-03-31 07:39

[[ … ]] 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.

查看更多
放我归山
4楼-- · 2019-03-31 07:42

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.

查看更多
登录 后发表回答