Bash : Why are spaces important here ?

Platform details:
Shell: bash
OS : Oracle Linux 6.4 (Same kernel as RHEL )

test1.sh is a very basic script which will loop until count variable reaches 15.

# cat test1.sh
count=5
while [ $count -le 15 ]
        do
                echo $count
                count=`expr $count + 1`
        done
#


# ./test1.sh
5
6
7
8
9
10
11
12
13
14
15
#
#

test2.sh below is almost same as test1.sh script except that 4 spaces denoted using <SPACE> below are missing and hence it doesn't work.
If you miss any one of these 4 space characters , the script won't work. Why ? I didn't expect this from a modern shell like BASH.

### the below are spaces in test1
count=5
while [<SPACE>$count -le 15<SPACE>]
    do
        echo $count
        count=`expr $count<SPACE>+<SPACE>1`
    done


# cat test2.sh
count=5
while [$count -le 15] # 2 spaces missing in this line
        do
                echo $count
                count=`expr $count+1` # another 2 spaces missing in this line
        done
#
# ./test2.sh
./test2.sh: line 2: [5: command not found

Because "[" is considered a command the same way ls is a command, not a special shell syntax. "[5" is a valid filename too -- if you had a file named "[5", it would run it, so if you don't mean "[5", don't tell it "[5" -- remember how shell syntax works, "command space arguments".

If you left spaces out of other commands, like lsfilename you certainly wouldn't expect that to work.

1 Like

[ is not any regular parenthesis like in other programming languages. It's equivalent of the test command. And all the keywords that follow [ are arguments passed to the program [ ; including the ] which [ interprets as the "end of arguments".

[ program may be found in /usr/bin or /bin

1 Like

There is no need to use an external command for integer arithmetic in bash (or any other POSIX shell):

count=$(( count + 1 ))

Or, in bash:

(( ++count ))
1 Like

Thank You CFAJohnson

It seems parantheses ( ) doesn't have the issue with spaces as I managed to use $((count+1)) without any issues.
So, [ character being misinterpreted as a command is not applicable for ( )

# cat test5.sh
count=5
while [ $count -le 15 ]
do
                echo $count
                count=$((count+1))
done

# ./test5.sh
5
6
7
8
9
10
11
12
13
14
15
#

BTW ..What is ++ operator as in (( ++count )) called ?

It's known as a pre-increment operator. It means, first increment the value in count and then use it.
On the contrary, there's a post-increment operator ((count++)); which means use it first and then increment it.

It will be clear in the following example:

[user@host ~]$ x=1
[user@host ~]$ y=$((++x))
[user@host ~]$ echo $x $y
2 2
[user@host ~]$ x=1
[user@host ~]$ y=$((x++))
[user@host ~]$ echo $x $y
2 1
[user@host ~]$
2 Likes

Thank You balajesuri.

That's the thing -- it's not being misinterpreted, it is a command.

$ whereis [

[: /usr/bin/[

$ ls -l /usr/bin/[

-rwxr-xr-x 1 root root 39424 Jun  5  2014 /usr/bin/[

$

It's usually a built-in as well, but the command must exist. The shell must treat it as a command.

( ) are not commands.

1 Like

Rather than whereis , use type ; It tells you what the shell will actually use:

$ type [
[ is a shell builtin

With the -a option, it gives all the possible commands that could be executed:

$ type -a [
[ is a shell builtin
[ is /usr/bin/[

[ is also a synonym for test .

4 Likes

For beginners, just to be VERY aware '[' and 'test' are NOT quite the same although they do a similar thing...
They cannot __cross-pollnate__.

Last login: Tue Jan 20 18:45:37 on ttys000
AMIGA:barrywalker~> var="Bazza..."
AMIGA:barrywalker~> if test "$var" == "Bazza..."; then echo "$var"; fi
Bazza...
AMIGA:barrywalker~> if [ "$var" == "Bazza..."; then echo "$var"; fi
-bash: [: missing `]'
AMIGA:barrywalker~> # Oh dear '[' and 'test' are not quite the same...
AMIGA:barrywalker~> if [ "$var" == "Bazza..." ]; then echo "$var"; fi
Bazza...
AMIGA:barrywalker~> if test "$var" == "Bazza..." ]; then echo "$var"; fi
-bash: test: too many arguments
AMIGA:barrywalker~> # Oh dear 'test' doesn't allow a closing square bracket...
AMIGA:barrywalker~> _
1 Like

The only difference is that [ requires a closing ] .

AMIGA:barrywalker~> if test "$var" == "Bazza..."; then echo "$var"; fi

Also note that test/[ uses = not == (though bash and ksh accept it).