15

As a primarily Java programmer, I find the bash if-then construct quite confusing, especially regarding whitespace. Can anyone explain why the first one works, but not the second or third?

#works
if [ -n $1 ]; then echo "parameterized"; fi

#output: ./links: Zeile 1: [-n: Kommando nicht gefunden.
if [-n $1 ]; then echo "parameterized"; fi

#output: ./links: Zeile 1: [: Fehlende `]'
if [ -n $1]; then echo "parameterized"; fi
1
  • 5
    Leaving a variable unquoted is the split+glob operator in shells. There are very few cases where you really want to leave variables unquoted. Commented Feb 28, 2014 at 11:30

2 Answers 2

25

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.

1
  • +1 for nice concise explanation of the '$1 does not mean "the value of parameter 1" stuff' -- this is the crucial thing that needs to be said about that, and the nitty-gritty you linked to could actually be improved by beginning it with what you said here.
    – Don Hatch
    Commented Aug 8, 2015 at 0:31
12

in fact the character [ is a built-in shell command that evaluate argument until the argument ]
bash use argument separate by space so if you want to call [ you need to put a space or it will try to execute the command [-n which is not a function or binary bash know (at least until you create one yourself).

When you write [-n $1 ] it means
[-n with arguments $1 and ] which make no sense
and if you put [ -n $1] it means
[ with argument -n and $1] but missing the ] argument of your function [ so it can't evaluate your expression

2
  • Hm, I've been using it for years, never thought of why it's this way and not any other. This makes total sense. Thank you for education.
    – Marcin
    Commented Feb 28, 2014 at 14:23
  • @Marcin I often asked myself the question and I recently read a create answer about where is cd ? and then I understand why a space was an obligation, because without it make no sense :D anyway I'm glad this help you
    – Kiwy
    Commented Feb 28, 2014 at 14:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .