1

To what value does an unquoted, undeclared variable expand to in order to return an exit status of 0?

Here is an example of a particular situation I ran into:

[ -n $var ]; echo $?
0

[ -n "$var" ]; echo $?
1

In both tests, the variable var is not declared. I could have saved me the hassle by testing with -z, where quoted or unquoted apparently doesn't make a difference, but I ran into this particular situation and I started wondering. I looked deeper into all the expansions that bash performs, but couldn't find any explanation for this behavior.

As a general rule I usually quote variables, but hopefully the reason of this behavior helps me better understand quoting.

twan163
  • 5,590
  • 4
  • 14
  • 17
  • `[ -n ]` is true (0) since `-n` is a non-empty string. Also note that `[ -z $var ]` would have been true for the same reason. I suspect there would be a duplicate question for this somewhere... – Kusalananda Oct 21 '20 at 12:03
  • In your case, you can do without the option `-n`: `[ "$var" ]` or `[ $var ]` – nezabudka Oct 21 '20 at 12:17
  • Related: [How does bash interpret the equal operator with no surrounding spaces in a conditional?](https://unix.stackexchange.com/questions/7655/how-does-bash-interpret-the-equal-operator-with-no-surrounding-spaces-in-a-condi) – steeldriver Oct 21 '20 at 13:17
  • @nezabudka, no, please don't tell people to use (the unquoted) `[ $var ]`, it breaks the moment `$var` contains more than one word – ilkkachu Oct 21 '20 at 13:22
  • 1
    On the other hand, `[ -z $var ]` works quoted or not as long as `$var` contains one or zero words. I think there was a post explaining that somewhere, but I can't find it so I'll leave this as an exercise... – ilkkachu Oct 21 '20 at 13:24
  • @ikkachu, Will it be okay?: `[ -n $var ]` or `[ -z $var ]`. "In your case...". Considering a certain example. – nezabudka Oct 21 '20 at 13:39

1 Answers1

5

The test, [ and [[ commands all act the same in that they do different things based on how many arguments they are given (excluding the closing ]/]]).

  • with 1 argument, the test will be successful if the argument is not empty
  • with 2 arguments, the arguments are treated as a unary operator (such as -n) and an operand.
  • with 3 arguments, the arguments are treated as an operand, a binary operator (such as =) and another operand
  • more than 3 arguments, test and [ give a "too many arguments" error, and [[ may give a different error (it has a more complicated parser).

Now, looking at the [ -n $var ] example, and comparing against the [[ construct. Since [[ does not do word splitting, it knows where variable values are. When var="", given [[ -n $var ]], after parameter expansion, bash will see [[ -n "" ]] which is clearly false.

But for test and [, word splitting is in effect. So [ -n $var ] becomes [ -n ] (the empty string disappears). Now, [ sees one argument which is a non-empty string, and is therefore true.

If you quote the variable: [ -n "$var" ], then you'll get the expected false result.

glenn jackman
  • 84,176
  • 15
  • 116
  • 168
  • There are similar confusing results when `var='*'` due to filename expansion. – glenn jackman Oct 21 '20 at 13:04
  • Thanks, helpful! Also because you indicate that the test behaves differently depending on the number of arguments. – twan163 Oct 21 '20 at 13:31
  • Also, refer to the canonical Q&A [Security implications of forgetting to quote a variable in bash/POSIX shells](https://unix.stackexchange.com/q/171346/4667) for ALL the details. – glenn jackman Oct 21 '20 at 17:45