The inconsistency is largely due to historical reasons.
The use of brackets as the conditional command came after the use of brackets in wildcard patterns. So at the time [ -n foo ]
came onto the scene, [foo]
already meant “a file whose name is f
or o
”. While few uses of the brackets operator in practice would have conflicted with realistic uses of the brackets in wildcards, the authors chose not to risk breaking existing scripts by changing the syntax. This design choice also made implementation easier: initially [
was implemented as an external command, which couldn't have been done if the space after [
had been optional. (Modern systems still have [
as an external command but almost all modern shells also have it built in.)
For similar reasons, your “working” code is actually incorrect in most circumstances. $1
does not mean “the value of parameter 1”: it means “take the value of parameter 1, split it into separate words at whitespace (or whatever the value of IFS
is), and interpret each word as a glob pattern”. The way to write “the value of parameter 1” requires double quotes: "$1"
. See When is double-quoting necessary? for the nitty-gritty. You don't need to understand this; all you need to remember is: always put double quotes around variable substitutions "$foo"
and command substitutions "$(foo)"
. Thus:
if [ -n "$1" ]; then …
In bash, ksh and zsh, but not in plain sh, you can also use double brackets. Single brackets [ … ]
are parsed like an ordinary command (and indeed [
is an ordinary command, albeit usually built in). Double brackets are a separate syntactic construct and you can omit the double quotes most of the time.
if [[ -n $1 ]]; then …
However, there is an exception: [[ "$foo" = "$bar" ]]
to test if the two variables have the same value requires double quotes around $bar
, otherwise $bar
is interpreted as a wildcard pattern. Again, rather than remember the details, you might as well use double quotes all the time.