5

I aim to understand the general concept of "variable attributes" hoping it will help me understand what is declare in Bash.

What is a variable attribute? Why would someone want to give an attribute to a variable? Why isn't just creating an variables and expanding them in execution be "enough" when working with variables?

jesse_b
  • 37,005

2 Answers2

10

Normally, a variable is a place to store a value. You assign a value to the variable (var="some value"), and after that you can recall the value with a variable expansion (writing "$var" is equivalent to writing "some value").

It's possible to make variables that do something special when you assign a value to them, or in other circumstances where the shell accesses variables. An attribute on a variable is an annotation that the shell stores next to the variable's name and value, which tells the shell to apply this special behavior.

One example

declare -i x tells the shell that x must contain integer values only. Normally, when you assign a value to a variable, the shell takes the string that results from expanding the right-hand side of the equal sign and stores it as the variable's value. But if the variable has the integer attribute, the shell parses that string as an arithmetic expression and stores the result of evaluating that expression. For example:

$ x=2+2; echo $x
2+2
$ declare -i x; x=2+2; echo $x
4
$ declare -i x; x=2+hello; echo $x
2
$ declare -i x; x=2+
bash: 2+: syntax error: operand expected (error token is "+")

(The third line with x=2+hello sets x to 2 because hello is a variable name which is not defined, and unset variables are silently interpreted as 0 by default.)

More examples

  • declare -l var declares that var must contain lowercase letters only. When the shell stores the value of the variable, it converts any uppercase letter to lowercase.
  • declare -u var does the conversion in the other direction.
  • declare -r var makes var read-only, which is also a special behavior of assignment: it causes every subsequent assignment to var to fail.
  • declare -x var causes var to be exported to the environment. For this attribute, the special behavior happens when bash runs an external command: external commands see an environment that contains the variables that the shell is exporting at the time the shell runs the external command.
8

From help declare:

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 the value of each NAME 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 the value of each NAME to upper case on assignment
    -x  to make NAMEs export

Note: declare can also be used for functions.

Each of these attributes has one or several uses:


-a - to make NAMEs indexed arrays (if supported)

This is not entirely necessary because setting a parameter as an array will automatically declare it as an indexed array. Using this could make your code more obvious and readable.


-A - to make NAMEs associative arrays (if supported)

AFAIK this is entirely necessary as attempting to set an associative array without first declaring it as such will result in an indexed array.

$ assoc=([foo]=bar)
$ declare -p assoc
declare -a assoc=([0]="bar")
$ unset assoc
$ declare -A assoc
$ assoc=([foo]=bar)
$ declare -p assoc
declare -A assoc=([foo]="bar" )

-i - to make NAMEs have the `integer' attribute

Useful if you want to ensure your parameter can only hold integers. This also allows you to perform arithmetic expansion on assignment.

$ declare -i a
$ a=foo
$ echo $a
0
$ a=1+1
$ echo $a
2

-l - to convert the value of each NAME to lower case on assignment

Will ensure that the value of your parameters will always be all lowercase. This is a pretty cool feature that I was unaware of and will probably use in the future. It eliminates the need for complex parameter expansion or using a separate utility like tr

$ declare -l foo=Bar
$ echo $foo
bar

-n - make NAME a reference to the variable named by its value

Like an indirect reference. This could eliminate the use of eval in a lot of scripts.

$ a=foo
$ declare -n b=a
$ echo $b
foo

-r - to make NAMEs readonly

This is a good feature. It could be especially useful for shell/environmental variables that you want to set once and ensure are not changed

$ declare -r foo=bar
$ echo $foo
bar
$ foo=baz
-bash: foo: readonly variable

-t - to make NAMEs have the `trace' attribute

I'm unsure about this one. I think it may only apply to functions.


-u - to convert the value of each NAME to upper case on assignment

Similar to -l but opposite

$ declare -u foo=bAr
$ echo $foo
BAR

-x - to make NAMEs export

Just another way to export variables to the environment.


jesse_b
  • 37,005