10

I understand test -n <expression> to return false if the expression evaluates to a string length of greater than 0. Why then does the following happen?

Macbook:~ echo ${#undeclared_variable}
0
Macbook:~ test -n $undeclared_variable && echo Foo
Foo

Bash 4.x

My expectation is that since test -n $undeclared_variable should evaluate false, that Foo should not be echoed. If I use [[ ]] instead of test, it works as expected.

1 Answers1

15

You need to quote your variables. Without quotes you are writing test -n instead of test -n <expression>. The test command has no idea that you provided a variable that expanded to nothing.

In the syntax of POSIX shells like bash, $VARIABLE outside of double quotes is the “split+glob” operator: the value of the variable is split into a list of whitespace-delimited words, and each word is interpreted as a wildcard pattern and replaced by the names of matching files (or left alone if there is no matching file). If the value of the variable is empty, the list of words after splitting is an empty list. To obtain the value of the variable without further processing, the variable expansion needs to be inside double quotes.

The reason [[ works is because it's a bash special syntax and not a command; within this special syntax, $VARIABLE means “value of VARIABLE” (except on the right-hand side of the pattern matching operators =, ==, != and =~). Using [ would function the same as test. The following does what you want:

test -n "$undeclared_variable" && echo "Foo"
jordanm
  • 42,678
  • Could you explain why test does not recognize that a variable was passed to the test command when not using quotes? – Gregg Leventhal Jul 06 '14 at 20:19
  • When not using quotes, it is equivalent to test -n, which returns true. – vinc17 Jul 06 '14 at 20:37
  • 1
    i understand, I want to know the mechanism behind that behavior, is it Bash's wordspliting, or quoting behaviors that are responsible, or is it the test command that makes the non-quoted variable string "disappear"? – Gregg Leventhal Jul 06 '14 at 20:38
  • 2
    [[ is special in bash. But for arbitrary commands, like test, an unquoted undefined variable is replaced by nothing (no arguments), while "$undefined_variable" is replaced by an argument that is the empty string (length = 0). – vinc17 Jul 06 '14 at 20:42
  • 1
    Note that you have the same behavior if the variable contains only spaces. This is just a consequence to word splitting in the particular case of 0 words. – vinc17 Jul 06 '14 at 21:00
  • 3
    Why does test -n evaluate true anyhow? I would think it would be false if passed nothing. – Gregg Leventhal Jul 07 '14 at 14:27
  • Why does test -n evaluate to true? This explains it best: https://unix.stackexchange.com/a/400895/306023 –  Sep 07 '18 at 21:50