Why isn't [ -n ] false like [ -n "" ]?

[ -n ] does not use the -n test.

The -n in [ -n ] is not a test at all. When there is only one argument between [ and ], that argument is a string that is tested to see if it is empty. Even when that string has a leading -, it is still interpreted as an operand, not a test. Since the string -n is not empty--it contains two characters, - and n, not zero characters--[ -n ] evaluates to true.

As Ignacio Vazquez-Abrams says, where string is a single argument, the test performed on string in [ string ] is the same as the test performed on it by [ -n string ]. When string happens to be -n, nothing special happens. The -n in [ -n ] and the second -n in [ -n -n ] are simply strings being tested for emptiness.

When there is only one argument between [ and ], that argument is always a string to be tested for nonemptiness, even if it happens to be named the same as a test. Similarly, when there are two arguments between [ and ] and the first of them is -n, the second one is always a string to be tested for nonemptiness, even if it happens to be named the same as a test. This is simply because the syntax for [ insists that a single argument between [ and ] or after -n is a string operand.

For the same reason that [ -n ] doesn't use the -n test, [ -z ] doesn't use the -z test.


You can learn more about [ in bash by examining the help for it. Notice that is a shell builtin:

$ type [
[ is a shell builtin

Thus you can run help [ to get help on it:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

For more information, including what tests are supported and how they work, you will have to see the help on test. When you run the command help test, you'll get a detailed list. Rather than reproduce it all, here's the part about string operators:

      -z STRING      True if string is empty.

      -n STRING
         STRING      True if string is not empty.

      STRING1 = STRING2
                     True if the strings are equal.
      STRING1 != STRING2
                     True if the strings are not equal.
      STRING1 < STRING2
                     True if STRING1 sorts before STRING2 lexicographically.
      STRING1 > STRING2
                     True if STRING1 sorts after STRING2 lexicographically.

Notice that -n STRING and just STRING do the same thing: they test if the string STRING is not empty.


[x] is equivalent to [ -nx] even if x starts with - provided there is no operand.

$ [ -o ] ; echo $?
0
$ [ -eq ] ; echo $?
0
$ [ -n -o ] ; echo $?
0
$ [ -n -eq ] ; echo $?
0

[ -n ] is true because the [ command (aka the test command) acts upon the number of arguments it is given. If it is given only a single argument, the result is "true" if the argument is a non-empty string. "-n" is a string with 2 characters, not empty, therefore "true".

Tags:

Bash