In bash, why does the if [ -n ] test return true for an empty string? [duplicate]
Any idea why echo
statement is being executed in both scenarios?
Outputs it worked
:
#!/bin/bash
rizwan=''
if [ -n $rizwan ]; then
echo "it worked"
fi
Replace -n
with -z
, and it outputs it worked
when expecting no output:
#!/bin/bash
rizwan=''
if [ -z $rizwan ]; then
echo "it worked"
fi
You may know it or not, but let's make it clear: [
is not a part of the if
syntax. I mean if [ …
behaves like if true
, if false
or if any_command
, where [
, true
, false
and any_command
are commands. They return exit status and this is what matters to if
.
Even if [
is a builtin (and it is in Bash), it behaves like a regular command. There's even a standalone [
executable (e.g. /usr/bin/[
) because it's required by POSIX.
This means [ -n whatever ]
is just a [
command with few arguments. Yes, ]
is just an argument, it's not a delimiter or anything like that. The command named [
just requires ]
to be its last argument. This way the code is more legible, ]
serves no other purpose.
The command test
is equivalent to [
. The only difference is test
does not require ]
.[ -n whatever ]
is equivalent to test -n whatever
.
If the shell expands $rizwan
to an empty string, the snippets [ -n $rizwan ]
and [ -z $rizwan ]
become [ -n ]
and [ -z ]
respectively. The [
command is not aware there was something between -n
(or -z
) and ]
in your code, it gets exactly two arguments. Both tests are treated as the [ STRING ]
syntax where STRING
is either -n
or -z
. This syntax tests if STRING
is not empty. Your tests always succeed because neither -n
nor -z
is an empty string.
To make the snippets work as [ -n STRING ]
or [ -z STRING ]
, you need to double-quote the variable. The shell, after expanding [ -z "$rizwan" ]
, will pass exactly three arguments to [
. They will be -z
, the expanded value of the variable (even if it's an empty string) and ]
. The tool will get all the arguments you wanted it to get.
If unquoted $rizwan
in your examples expanded to more than one word, [
would get more than three arguments. In general the final result would depend on the variable content and the implementation of [
.
Compare Why does my shell script choke on whitespace or other special characters?.
Note [[
in Bash is different. It's a keyword, it's integrated with the shell parser, it changes some rules, it's actually aware of variables and quotes between [[
and ]]
. If you used [[
instead of [
(and consequently ]]
instead of ]
), then your code would work as you expected. See this answer.
[
is the portable one.
As variable rizwan
is empty, your if
command becomes:
if [ -n ]
The syntax [ STRING ]
tests if the string is empty (no characters).
So in your case of [ -n ]
, the string -n
is evidently not-empty.