-1

From the man test pages:

-n STRING
    the length of STRING is nonzero

STRING equivalent to -n STRING

-z STRING
    the length of STRING is zero

Executing [ -n $foo ], [ -n ], [ -z $foo ], [ -z ], all return true. Is this just sloppy return value? An unset variable is not a STRING. The total omission of an expression is not either. And even if it WERE being somehow "coerced" into one, I'm disinclined to think it's length could be both zero and non-zero simultaneously.

Indeed, both

if [ -n ] && [ -z ]; then 
   echo 1; 
fi
# AND
if [ -n $foo ] && [ -z $foo ]; then 
   echo 1; 
fi

...both yield "1". Why? What's going on under the hood here?

NerdyDeeds
  • 121
  • 5
  • See the comment under your earlier question: ["With one argument, the test is true if the argument is a non-empty string. "-n" is a non-empty string, so the result is true and "1" is echoed."](https://unix.stackexchange.com/questions/748688/shell-script-not-picking-up-variable-in-if-statement/748699?noredirect=1#comment1424358_748699). That's the part where the man page says the expression can be: "STRING -- same as -n STRING". – ilkkachu Jun 14 '23 at 18:56
  • 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 Jun 14 '23 at 22:25

1 Answers1

4

The problem

You misunderstand how the test conditions are parsed.

First bash determines the number of arguments:

  • nothing between [ and ] is false
  • exactly one argument (which is not the empty argument) between them is always true, no matter the content of the argument; false in case of an empty argument ([ '' ])
  • only if there is more than one argument then shell looks at the content.

-z and -n require a second argument. As there is none in your case, they are not treated as -z / -n but just as any piece of content which results in true.

The solution

You have to quote your variables (as you should nearly always do). Then an empty variable is seen as an empty argument instead of just being removed completely.

Hauke Laging
  • 88,146
  • 18
  • 125
  • 174
  • 2
    [Double brackets are better](https://www.baeldung.com/linux/bash-single-vs-double-brackets). – Aaron D. Marasco Jun 14 '23 at 19:22
  • 3
    [ "exactly one argument" ] is true only if that one argument is non-empty. `[ "$var" ]` is short for `[ -n "$var" ]`. `[ '' ]` or `[ "" ]` or `[ "$empty" ]` where `[` is passed 3 arguments: `[`, the empty string and `]`, returns false. Same result, but not the same invocation as in `[ ]` where `[` is passed only 2 arguments: `[` and `]`. – Stéphane Chazelas Jun 14 '23 at 19:50