jjno91
jjno91

Reputation: 653

bash loop to assign variables values without overwriting previously declared variables

The setup we currently have is to declare several variables in a config file and then we run some processes that depend on those variables. Additional functionality that I'm trying to provide is a default_variables script that will give default values to all the variables in the config files, while not overriding anything that is declared in the config file.

Here is my current code:

#!/bin/bash -x

# allows var to be used as an associative array
declare -A var

# variable declarations must be of the form:
# var[var_name]=var_value
# after processing loop, the above will be evaluated as:
# export var_name=var_value

# declare default variables
var[a]=a_val
var[b]=b_val
var[c]=c_val
var[d]=d_val

# iterate on associative array indices
for default_var in `echo "${!var[@]}"`
do

test_var=\$${default_var}

  # only export variables that have not been previously declared
  if [[ -z `eval $test_var` ]]
  then

    # export each index name as a variable
    export $default_var=${var[$default_var]}
  fi
done

The error I'm currently getting is that the eval statement tries to execute the value of a variable if it was declared before the script was ran. For instance, if the above script were ran twice then the error would be:

-sh: a_val: command not found
-sh: b_val: command not found
-sh: c_val: command not found
-sh: d_val: command not found

Now I am definitely up for a more elegant way of solving this problem, but this is the best I could come up with. Finally, I know that exporting the values normally and checking each variable individually to see if they are allocated would be a more "correct" way of doing this, but I don't want to exponentially bloat the script and I don't want people to have to copy the logic every time they want to add a new variable to the script and I can't just run the default_variables script before the config file allowing it to override the default values because some default values depend on config values.

Thanks in advance for any help!

Upvotes: 2

Views: 1238

Answers (3)

konsolebox
konsolebox

Reputation: 75568

This is how I would do it:

# iterate on associative array indices (no need to do `echo ...`)
for default_var in "${!var[@]}"
do
    # skip variables that have been previously declared (even if their value is null)
    if [[ -z ${!default_var+.} ]]
    then
        # export each index name as a variable (the safer way with quotes)
        export "$default_var=${var[$default_var]}"
    fi
done

Upvotes: 1

gniourf_gniourf
gniourf_gniourf

Reputation: 46883

#!/bin/bash

# allows var to be used as an associative array
declare -A var

# variable declarations must be of the form:
# var[var_name]=var_value
# after processing loop, the above will be evaluated as:
# export var_name=var_value

# declare default variables
var[a]=a_val
var[b]=b_val
var[c]=c_val
var[d]=d_val

# iterate on associative array indices
for default_var in "${!var[@]}"; do     #<=== no ugly `echo ...` needed
    [[ "${!default_var}" ]] && continue #<=== indirect expansion
    # export each index name as a variable
    export "$default_var=${var[$default_var]}" #<=== use quotes!
done

echo "a=$a"
echo "b=$b"
echo "c=$c"
echo "d=$d"

I called this script banana:

$ ./banana
a=a_val
b=b_val
c=c_val
d=d_val
$ a=gorilla ./banana
a=gorilla
b=b_val
c=c_val
d=d_val

Now this only checks if the variable is unset or empty, that is a user can't explicitly make a variable empty:

$ a= ./banana
a=a_val
b=b_val
c=c_val
d=d_val

To support this, you can instead do:

for default_var in "${!var[@]}"; do     #<=== no ugly `echo ...` needed
    if ! declare -p "$default_var" &>/dev/null; then
    # export each index name as a variable
        export "$default_var=${var[$default_var]}" #<=== use quotes!
    fi
done

so that:

$ a= ./banana
a=
b=b_val
c=c_val
d=d_val

Upvotes: 2

rici
rici

Reputation: 241901

Don't use eval, which causes its argument to be executed as a command. All you want is the value of the variable named by $default_var. There is a perfectly good syntax for indirect variable reference:

if [[ -z ${!default_var} ]]

Although you really wanted to test for non-empty. -z tests for empty. So it should be.

if [[ -n ${!default_var} ]]

Also, this is overcomplicated, and arguably incorrect:

for default_var in `echo "${!var[@]}"`

Just write:

for default_var in "${!var[@]}"

Upvotes: 2

Related Questions