I've been trying to compare whether two numbers in Bash are equal (and print a message if they are equal), but I'm getting some strange error messages for this simple program:
#!/bin/bash fun2 (){ $x = 3 //#prog.sh: line 4: =: command not found if [x == 3] then //#prog.sh: line 6: [x: command not found echo "It's 3!" fi } fun2
The corresponding errors are shown below the lines that caused those errors.
It must be:
if [ $x -eq 3 ]; then .....
If you prefer a more readable and self-explanatory code, use this syntax:
if test $x -eq 3; then .....
Explanation:
To compare integers you must use those operators (copied from man test
):
INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2 INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2 INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2 INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2 INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2 INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2
operators == and != are for string comparison only.
For information: "[" command is an alias for system "test" command.
Shell scripting wasn't really meant to be a full language, and it is heavily dependent upon other external commands to work.
Variables use a sigil which is that $
in front of them. A lot of shell scripting languages use variable sigils to help distinguish between a string and a variable.
For example:
foo=foo_value echo foo foo $foo foo
Will print:
foo foo foo_value foo
Note that quotes are not necessary for the echo statement for strings. Windows Batch shell is very similar:
set foo = foo_value echo foo foo %foo% foo
As you can see, the sigil is used when the variable is supposed to be expanded, but not when you define it. That's because Unix shells are intelligent shells. They munge the command line before it is even executed. The shell will substitute the environment variables before execution:
foo=bar $foo="whose value is this" # Note the dollar sign! echo The value of foo is $foo echo The value of bar is $bar
This will print out:
The value of foo is foo The value of bar is whose value is this
If you use the set -xv
command, you'll see that $foo="whose value is this"
is expanded to bar=whose value is this"
before it is executed.
In Bourne style shells like KornShell and Bash, the if
statement isn't what you think it is. The if
command executes the statement, and will select the if clause if that command returns a zero value. For example:
cat "foo" > my_file # Create a one line file with the string foo in it. if grep -q "foo" my_file # grep command will return a zero exit code if it finds foo then echo "The string 'foo' is in file my_file" fi
Notice that the if clause isn't a Boolean statement. It's an actual command that is executed.
Somewhere early in Unix development, the test
command was created. You can do a man test and see how to use it.
The test
command allows you to do this:
foo=3 bar=3 if test foo -eq bar then echo "foo and bar are equal" else echo "foo and bar are not equal" fi
If you do this:
$ ls -li /bin/test /bin/[
You will see that a command called [
actually exists, and is a hard link to the test
command. This was created to make an if
statement look more like a if statement you'll see in regular programming languages:
foo=3 bar=3 if [ foo -eq bar ] then echo "foo and bar are equal" else echo "foo and bar are not equal" fi
It is the exact same script as above, but with [
instead of test
.
This explains why you need the dash in many tests (it's a parameter to the test
command, and parameters start with a dash!). It also explains why you need spaces around the [
and ]
. These are actual Unix commands, and Unix commands must have white spaces around them, so the shell can process them.
Another issue is: Why does the shell have two different sets of tests for strings and numbers? That's because strings may contain nothing but digits, but are not really numeric. For example, if you do inventory, you might have a part number 001
and 01
. Numerically, they're equal, but as strings, they're two different strings. There is no way for the shell script to know. Instead, you must let the shell script know if it's a numeric comparison or a string comparison.
Perl has similar issues since you don't declare variables as numeric or non-numeric:
Shell Script Perl Boolean Operator Numeric String Numeric String =================== ======= ====== ======= ====== Equals -eq = == eq Not Equals -ne != != ne Greater Than -gt > > gt Less Than -lt < < lt Greater or Equals -ge >= >= ge Less Than or Equals -le <= <= le
You can try a few other things:
$ echo "*" # Echos the asterisk $ echo * # No quotes: Prints all files in current directory
Notice again the shell expands the *
before executing the echo command. This is the main difference between a shell script and a typical programming language. The shell first does expansion (fill in environment variables, glob substitution, and run sub-commands) before it actually executes the command.
The set -xv
will show you what command is being executed, and how the shell expands the command before executing. Doing set +xv
will shut that off. Play around with that, and you'll soon understand the shell a bit better.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With