In bash scripting, what's the different between declare and a normal variable?

From help -m declare:

NAME

    declare - Set variable values and attributes.

SYNOPSIS

    declare [-aAfFgilnrtux] [-p] [name[=value] ...]

DESCRIPTION

    Set variable values and attributes.

    Declare variables and give them attributes. If no NAMEs are given, display the attributes and values of all variables.

    Options:

      -f
        restrict action or display to function names and definitions
      -F
        restrict display to function names only (plus line number and source file when debugging)
      -g
        create global variables when used in a shell function; otherwise ignored
      -p
        display the attributes and value of each NAME

    Options which set attributes:

      -a
        to make NAMEs indexed arrays (if supported)
      -A
        to make NAMEs associative arrays (if supported)
      -i
        to make NAMEs have the ‘integer’ attribute
      -l
        to convert NAMEs to lower case on assignment
      -n
        make NAME a reference to the variable named by its value
      -r
        to make NAMEs readonly
      -t
        to make NAMEs have the ‘trace’ attribute
      -u
        to convert NAMEs to upper case on assignment
      -x
        to make NAMEs export

    Using ‘+’ instead of ‘-’ turns off the given attribute.

    Variables with the integer attribute have arithmetic evaluation (see the let command) performed when the variable is assigned a value.

    When used in a function, declare makes NAMEs local, as with the local command. The ‘-g’ option suppresses this behavior.

    Exit Status:
    Returns success unless an invalid option is supplied or a variable assignment error occurs.

SEE ALSO

    bash(1)

IMPLEMENTATION

    GNU bash, version 4.3.11(1)-release (i686-pc-linux-gnu)
    Copyright (C) 2013 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>


So, declare is used for setting variable values and attributes.

Let me show the use of two attributes with a very simple example:

$ # First Example:
$ declare -r abc=ok
$ echo $abc
ok
$ abc=not-ok
bash: abc: readonly variable


$ # Second Example:
$ declare -i x=10
$ echo $x
10
$ x=ok
$ echo $x
0
$ x=15
$ echo $x
15
$ x=15+5
$ echo $x
20

From the above example, I think you should understand the usage of declare variable over normal variable! This type of declareation is useful in functions, loops with scripting.

Also visit Typing variables: declare or typeset


abc=ok assigns a value to the variable abc. declare abc declares a variable called abc. The two can be combined as declare abc=ok.

In bash, like other shells, string and array variables don't need to be declared, so declare isn't necessary unless you want to pass options, e.g. declare -A abc to make abc an associative array or declare -r to make a variable read-only. However, inside a function, declare does make a difference: it causes the variable to be local to the function, meaning that the value of the variable outside the function (if any) is preserved. (Unless you use declare -g, which makes the variable not local; this is useful when combined with other options, e.g. declare -gA to create a global associative array in a function.) Example:

f () {
  declare a
  a='a in f'
  b='b in f'
  echo "From f: a is $a"
  echo "From f: b is $b"
}
a='Initial a'
b='Initial b'
f
echo "After f: a is $a"
echo "After f: b is $b"

Output:

From f: a is a in f
From f: b is b in f
After f: a is Initial a
After f: b is b in f

Another thing you can do with the declare builtin is

The declare builtin is unique to bash. It's strongly inspired and very close to ksh's typeset builtin, and bash provides typeset as a synonym of declare for compatibility. (I don't know why bash didn't just call it typeset). There's a third synonym, local. There's also export, which is the same as declare -x, again for compatibility (with every Bourne-style shell).