Tricks of declaring dynamic variables in Bash
Tags: bash
Published: 2021-04-25 06:22:14

In any programming languages, it is a common practice to define a variable with the result of some computation and refer to it later when needed. In Bash, it's pretty straight forward to define variables as follows:
```bash
> FOO=bar
> echo $FOO
bar
```

But what if you want to define a variable, of which name is dynamic? This could be useful when the variable name is only known during runtime, as user input. Something like this:

```bash
# bash variables names are stored in variables.txt
# as user input
> cat > variables.txt <<EOF
FOO1=bar1
FOO2=bar2
FOO3=bar3
EOF

> do_some_magic

> echo $FOO1
bar1

> echo $FOO2
bar2

> echo $FOO3
bar3
```

## Trick 1 `declare` dynamic variables
`declare` is a built function within Bash. It is the magic here to define variables with dynamic names. A peak of `declare`'s man output:

```sh
declare: declare [-aAfFgiIlnrtux] [-p] [name[=value] ...]
    Set variable values and attributes.

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

This is what you need to do:

```bash
> var_name=FOO1
> declare $var_name="bar1"
> echo $FOO1
bar1
```

If you replace the above `do_some_magic` function with:
```bash

while IFS='=' read -ra line; do
  key=${line[0]}
  value=${line[1]}
  declare "$key"="$value"
done < variables.txt 

```
you will get:

```
> echo $FOO1
bar1

> echo $FOO2
bar2

> echo $FOO3
bar3
```

Super cool, right?

But thing gets tricky when you want to modulerize the above `while` loop as a function for usage in multiple places of your code. The following is what I did:

```bash
function define_dynamic_variables() {
  while IFS='=' read -ra line; do
    key=${line[0]}
    value=${line[1]}
    declare "$key"="$value"
  done < variables.txt 
}
```
But when I run the above function, the result is totally unexpected:

```bash
> define_dynamic_variables
> echo $FOO1
bash: FOO1: unbound variable
```

What's going on?

## Trick 2 declare is local when inside function

Why the same piece code works without a function but not when inside a function? 

A dive deep into the output of `help declare` shows:

```bash
When used in a function, `declare' makes NAMEs local, as with the `local'
    command.  The `-g' option suppresses this behavior.
```
The nuance is clearly explained by the help output. It basically says if you need to define a variable that live outside of the function scope, the `-g` option is needed, otherwise, the variable is defined local to the function scope and hence invisible after the function runs.

After fixing the function to be:

```bash
function define_dynamic_variables() {
  while IFS='=' read -ra line; do
    key=${line[0]}
    value=${line[1]}
    declare -g "$key"="$value"
  done < variables.txt 
}
```

Everything back to normal:

```bash
> define_dynamic_variables
> echo $FOO1
bar1
```

## Trick 3 export dynamic variables as environment variable
Sometimes you might want to define the dynamic variable and export it as environment variable at the same time. You might come up with:

```bash
declare FOO=bar
export FOO
```

While it works, a more idiomatic why is to use the `-x` option:

```bash
declare -x FOO=bar  #when define the variable outside function
declare -gx FOO=bar #when define the variable inside function
```

# Summary
Today, we learned 3 tricks related to defining dynamic variables in Bash:
* define dynamic variables with `declare`
* variable defined by declare is local when inside function
* export dynamic variables as environment variable

Last but not least, as RTFM stands, always refer to manual when something unexpected happens and this sometimes turns out to be more useful than simply googling online.