Why are bash tests so picky about whitespace?

8,053

Solution 1

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.

Solution 2

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

Share:
8,053

Related videos on Youtube

Konrad Höffner
Author by

Konrad Höffner

Desktop CPU: Intel Core i9 10900k, Arctic Liquid Freezer 120 Mainboard: Gigabyte Gaming X Z490 RAM: 32 GB Kit + 16GB Corsair Vengeance LPX black DDR4-3000 DIMM CL15 Dual Kit GPU: Nvidia GeForce RTX 3070 PSU: EVGA SuperNOVA 850 G2 SSD: Samsung 960 Evo M.2 NVMe 500 GB & Samsung 840 Evo 250 GB Monitor: LG 27UD58P-B Headphones: Beyerdynamic DT 880 {Pro 250 Ohm, 600 Ohm} Audio Interface: Focusrite Scarlett 2i2 Headphone Amplifier: Objective 2 Microphone: Rode NT1-A OS: Arch Linux 64 Bit & Windows 10 64 Bit Laptop Model: Dell E6530 OS: Arch Linux

Updated on September 18, 2022

Comments

  • Konrad Höffner
    Konrad Höffner almost 2 years

    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
    
    • Admin
      Admin over 10 years
      Leaving a variable unquoted is the split+glob operator in shells. There are very few cases where you really want to leave variables unquoted.
  • BrettRobi
    BrettRobi over 10 years
    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.
  • S edwards
    S edwards over 10 years
    @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
  • Don Hatch
    Don Hatch almost 9 years
    +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.