$ a=""
$ test -n $a
$ echo $?
0
$ test -n a
$ echo $?
0
$ test -n "$a"
$ echo $?
1
What is the difference between them? Why are the results different?
When $a is empty, test -n $a expands to this:
'test' '-n'
When test only receives one argument like this, it does the default test, which is probably the source of your confusion. The default test is the same as -n, i.e. the following two are equivalent:
test string
test -n string
So your test is whether the string -n has a length greater than zero. It does, so you get a 0 exit status.
Your next example:
'test' '-n' 'a'
tests whether the length of the literal string a is greater than zero. It is, so you get a 0 exit status again.
The final test is probably what you wanted all along. It expands to this:
'test' '-n' ''
Now two arguments are passed, but the second one is empty, so the -n test fails and you get a 1 exit status.
Always quote your variables!
You can view how the shell expands your statement using set -x:
$ set -x
$ test -n $a
+ test -n
$ test -n "$a"
+ test -n ''
Let's play a game called "Count the arguments". How many arguments does test receive in the call
test -n $a
If you said "2" (meaning -n and $a), sorry, that's incorrect. The correct answer is "We don't know".
We don't know because we don't know what the value of the parameter a is. If a=foo, then test receives two arguments, -n and foo. If a="foo bar", then test gets three arguments after word-splitting takes place, -n, foo, and bar.
What if a=*? The if you said "2, -n and *", you get points for trying, but you are still wrong. That's because the result of expanding $a, if it contains certain characters like *, undergoes not just word-splitting, but pathname expansion as well. There is no way to tell how many arguments test will get without knowing how many files are in the current working directory, because * will expand to a sequence of file names, one word per file.
By contrast, test -n "$a" always passes two arguments to test. The quoted parameter expansion is not subject to word-splitting or pathname expansion, so whatever value a has (foo, foo bar, or *), that exact string is always passed to test as a single argument.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With