4
#! /bin/bash

echo "Please input 2 nums: "

read a b

if [ -z $b ]; then
        echo b is zero !
fi

if [ -n $b ]; then
        echo b is non-zero !
fi

when run the script, only input 1 number, and leave the other empty, then b is supposed to be null. but the result is both echo is printed.

-laptop:~$ ./test.sh 
Pleaes input 2 nums: 
5 
b is zero !
b is non-zero !

b is both null and non-null ?! Could anyone comment on this ? Thanks !

~

Adam Liss
  • 47,594
  • 12
  • 108
  • 150
Yifan Zhang
  • 1,461
  • 3
  • 12
  • 19
  • 1
    +1 for showing your code, and your actual output. Good luck! – shellter Feb 28 '12 at 16:29
  • If you're writing only for Bash, then you might want to replace the `[` conditionals with `[[`. Recommended reading here : http://mywiki.wooledge.org/BashFAQ/031 – Daniel Kamil Kozar Feb 28 '12 at 16:57
  • You have a race condition here. The file size could change on disk between the two `if` tests. You should use `else` instead, if your code actually has this form. – Warren Young Feb 29 '12 at 19:45

3 Answers3

4

Replace

if [ -z $b ]; then

with

if [ -z "$b" ]; then

And do the same in the other if condition as well.

See http://tldp.org/LDP/abs/html/testconstructs.html for some interesting tests.

Adam Liss
  • 47,594
  • 12
  • 108
  • 150
  • yes, it works ! thanks. but I wonder why "" makes so differences ? – Yifan Zhang Feb 28 '12 at 16:34
  • 1
    Without the quotes, an uninitialized variable evaluates as _nothing_ so the shell evaluates `if [ -z ]`, which is true because the "-z" is parsed as a (non-empty) string, which evaluates to `true`. – Adam Liss Feb 28 '12 at 16:39
  • 3
    Bottom line: always quote your stuff where it does not interfere with other desired outcomes (e.g. expansion to multiple args). – jørgensen Feb 28 '12 at 16:49
  • @AdamLiss is right: the shell first counts the number of arguments inside `[ ... ]`. If it's just one argument, the result is true if the argument is not empty. As [the man page says](http://www.gnu.org/software/bash/manual/bashref.html#index-test-122) "The `test` and `[` builtins evaluate conditional expressions using a set of rules based on the number of arguments." – glenn jackman Feb 28 '12 at 20:27
3

It's all in the quotes. I don't remember where, but someone explained this recently on SO or USE - Without the quotes it doesn't actually do an empty/non-empty string test, but just checks that -n or -z are non-empty strings themselves. It's the same test that makes this possible:

$ var=-n
$ if [ "$var" ]
then
    echo whut
fi

Returns whut.

This means you can also have a sort of functional programming:

$ custom_test() {
    if [ "$1" "$2" ]
    then
        echo true
    else
        echo false
    fi
}

$ custom_test -z "$USER"
false
$ custom_test -n "$USER"
true
l0b0
  • 55,365
  • 30
  • 138
  • 223
1

The -n test requires that the string be quoted within the test brackets. Using an unquoted string with ! -z, or even just the unquoted string alone within test brackets normally works, however, this is an unsafe practice. Always quote a tested string.

$ b=''
$ [ -z $b ] && echo YES  # after expansion: `[ -z ] && echo YES` <==> `test -z && echo YES`
YES
$ [ -n $b ] && echo YES  # after expansion: `[ -n ] && echo YES` <==> `test -n && echo YES`
YES

test against nothing, yield true.

kev
  • 155,172
  • 47
  • 273
  • 272
  • Thanks for your answer. But as you mention, unquoted string in test normally work, but why this case fails ? Could you give some hints ? – Yifan Zhang Feb 28 '12 at 16:39