13

My problem can be simplified down to making the following script work (which takes one command line argument):

#!/bin/bash
if ["$1" == "0"]; then
    echo "good"
else
    echo "bad"
fi

This should print good when I run script 0, but I can't get it to. I've tried various combinations of quotes around the numbers, and I've tried =, ==, and -eq. So... bash, how does it work?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
imMAW
  • 173
  • 1
  • 1
  • 6

3 Answers3

19

The [ is actually a command. Do a ls /bin/[ or an ls /usr/bin/[. You'll see it's actually an executable file.

The [...] is from the old Bourne shell days. The if command executes the statement, and if the exit code of that statement is a zero, the statement is considered true and the if clause is executed. If the exit code is not zero, the else clause is executed (if present).

Try these:

$ date
Fri May 18 00:04:03 EDT 2012
echo $?   #Prints the exit code of the date command
0

$ date -Q  #Shouldn't work, I hope...
date: illegal option -- q
usage: date [-jnu] [-d dst] [-r seconds] [-t west] [-v[+|-]val[ymwdHMS]] ... 
    [-f fmt date | [[[mm]dd]HH]MM[[cc]yy][.ss]] [+format]
$ echo $?    #Exit code for the date command
1

You can see that date is a valid command and returns an exit code of 0 (the value of $?), but date -Q isn't valid, and returns an exit code of 1.

Now let's try them in the if statement:

if date
then
   echo "I've successfully printed out the date!"
else
   echo "I made a mistake in the command"
fi

Now try this:

if date -q
then
   echo "I've successfully printed out the date!"
else
   echo "I made a mistake in the command"
fi

Originally, the [...] was an alias for the test command. The following are equivalent:

if test -f /bin/ls    #Does a file called /bin/ls exist?
then
   echo "There's a /bin/ls file"
fi

and

if [ -f /bin/ls ]
then
   echo "There's a /bin/ls file"
fi

This is why it's very important to put spaces around the [ and ]. Because these are actually commands. In BASH, there's built into the shell, but they are commands. That's also why all the test parameters (things like -f, -z, and -eq) all are prefixed with dashes. They were originally parameters for the test command.

alexey
  • 453
  • 6
  • 15
David W.
  • 105,218
  • 39
  • 216
  • 337
  • Wow, thanks for the informative answer. I thought it was just part of the bash syntax. – imMAW May 18 '12 at 04:36
  • It is part and isn't part of bash syntax. It's a built-in command, like `read` or `echo`. These also are now built into the shell, but the executables still exist in the `/bin` or `/usr/bin` directories. – David W. May 18 '12 at 05:02
  • It's now a built-in command in bash, but it's still just a synonym for `test`, not syntax. – chepner May 18 '12 at 12:28
16

Use a space between the bracket and the argument

$ cat x
#!/bin/bash
if [ "$1" == "0" ]; then
    echo "good"
else
    echo "bad"
fi

$ bash x 0
good
nbro
  • 15,395
  • 32
  • 113
  • 196
Tiago Peczenyj
  • 4,387
  • 2
  • 22
  • 35
12

Use double parentheses for arithmetic comparisons, then you don't need to worry about quotes and spacings, for example:

#!/bin/bash 
if (($1 == 0)); then 
    echo "good" 
else 
    echo "bad" 
fi 

General rule: use (( )) for arithmetics and [[ ]] for text and patterns.
As others have said, [ is old Bourne shell syntax and there are few reasons for using it any more.

cdarke
  • 42,728
  • 8
  • 80
  • 84