You always need quotes around variables in all list contexts, that is everywhere the variable may be expanded to multiple values unless you do want the 3 side effects of leaving a variable unquoted.
list contexts include arguments to simple commands like
for i in <here>, assignments to arrays... There are other contexts where variables also need to be quoted. Best is to always quote variables unless you've got a very good reason not to.
Think of the absence of quotes (in list contexts) as the split+glob operator.
echo $test was
The shell behaviour is confusing to most people because in most other languages, you put quotes around fixed strings, like
puts("foo"), and not around variables (like
puts(var)) while in shell it's the other way round: everything is string in shell, so putting quotes around everything would be cumbersome, you
echo test, you don't need to
"echo" "test". In shell, quotes are used for something else: prevent some special meaning of some characters and/or affect the behaviour of some expansions.
[ -n $test ] or
echo $test, the shell will split
$test (on blanks by default), and then perform filename generation (expand all the
*, '?'... patterns to the list of matching files), and then pass that list of arguments to the
Again, think of it as
"[" "-n" glob(split("$test")) "]". If
$test is empty or contains only blanks (spc, tab, nl), then the split+glob operator will return an empty list, so the
[ -n $test ] will be
"[" "-n" "]", which is a test to check wheter "-n" is the empty string or not. But imagine what would have happened if
$test was "*" or "= foo"...
[ -n "$test" ],
[ is passed the four arguments
"]" (without the quotes), which is what we want.
[ makes no difference, it's just that
echo outputs the same thing whether it's passed an empty argument or no argument at all.
See also this answer to a similar question for more details on the
[ command and the