0

I have a set of variables populated eg

var1="test"
var2="test2"

I have a .env file as follows

var1
var2
var3=test3

I need to read the .env file and append the value of each variable to the relevant line unless the line already has a value. So what I would end up with in my .env is

var1=test
var2=test2
var3=test3

what I have so far is:

function populate_envs {
      while IFS= read -r line || [[ -n "$line" ]]; do
        if [[ $line =~ "=" ]]; then
          printf '%s\n' "$line"
        else
          printf '%s=$${%s}\n' "$line" "$line"
        fi
      done <$1
    }

I'm currently printing the values to console so as not to change my file at the moment.

the problem is $${%s} is not working neither is $%s. I don't know how to get it to transpose the actual variable value.

  • See [BashFAQ/006 (How can I use variable variables (indirect variables, pointers, references) or associative arrays?)](https://mywiki.wooledge.org/BashFAQ/006) and [Indirect variable assignment in bash](https://stackoverflow.com/q/9938649/4154375). – pjh Mar 10 '23 at 21:37

3 Answers3

2

That's just not how Bash works. The correct line would be:

printf '%s=%s\n' "$line" "${!line}"

Explanation: Bash expands variables before running the command, so you cannot use whatever printf expands %s to as the variable name. Also, variables are not allowed at all inside single quotes. But most importantly, Bash doesn't support indirection through $${var}. Lucky for you, Bash does support indirection in two other ways. If you have a variable a=1 and you want to reference it indirectly, you can either do:

  1. b=a; echo ${!b}. The exclamation mark tells Bash to use the value of b as the name of the variable to actually use
  2. declare -n b=a; echo $b. The -n option tells Bash to declare b as a reference to a, so whenever b is referenced, the value of a is used
Verpous
  • 588
  • 3
  • 7
1

One idea using bash namerefs:

populate_envs() {
    while read -r line
    do
        [ -z "${line}" ] && continue                      # skip blank/empty lines
        IFS='=' read -r attrib value <<< "${line}"        # split line on "=" delimiter
        declare -n _var="${attrib}"                       # define nameref variable
        echo "${attrib}=${value:-${_var}}"                # print new line; if $value is empty then substitute the current value of the nameref variable
    done < "$1"
}

Taking for a test drive:

var1="test"
var2="test2"

populate_envs .env

This generates:

var1=test
var2=test2
var3=test3
markp-fuso
  • 28,790
  • 4
  • 16
  • 36
  • 1
    First time I have seen the use of a `nameref` variable. Thanks for the detail in the comment. – j_b Mar 10 '23 at 18:45
  • Same here. Fairly interesting. I used `eval` a lot to do similar job. Apparently `nameref` is better. – Taylor G. Mar 11 '23 at 07:02
0

Another solution using eval

while read -r line; do
    eval echo "$line"
done < <(sed -E 's/^[^=]+$/&=$&/' .env)

The .env file is first processed by sed. Essentially, if a variable has nothing assigned, it will be turned into var=$var format, e.g.

var1=$var1
var2=$var2
var3=test3

Then eval each line to generate final result, e.g.

var1=test
var2=test2
var3=test3
Taylor G.
  • 661
  • 3
  • 10