Check whether one number equals another number in Bash

Bash

Bash Problem Overview


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.

Bash Solutions


Solution 1 - Bash

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.

Solution 2 - Bash

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.

Solution 3 - Bash

For the first error message, remove the dollar sign $ and no spaces are allowed around the equal sign =

x=3

For the second error message, insert a space and a dollar sign $ before x and a space after 3

if [ $x -eq 3 ]

As loentar correctly pointed out, it must be -eq and neither = nor ==.

Solution 4 - Bash

  • the spaces are mandatory in the [ ] expression, so

    if [ $x == 3 ]; then

  • you need a sigill on variables in the [ ] tests

Solution 5 - Bash

For clarity, use == for equality rather than = even though both work. The former encourages the use of [[ and the latter can be confused with an assignment. Use (( … )) or -lt and -gt for numerical comparison.

if [[ "${my_var}" == "val" ]]; then
  do_something
fi

if (( my_var == 3 )); then
  do_something
fi

if [[ "${my_var}" -gt 3 ]]; then
  do_something
fi

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAnderson GreenView Question on Stackoverflow
Solution 1 - BashloentarView Answer on Stackoverflow
Solution 2 - BashDavid W.View Answer on Stackoverflow
Solution 3 - BashOlaf DietscheView Answer on Stackoverflow
Solution 4 - BashGilles QuenotView Answer on Stackoverflow
Solution 5 - BashFilip SemanView Answer on Stackoverflow