Bash – How to Set Environment Variables Dynamically

bashenvironment-variablesshell-script

I want my script to read a file containing key/value pairs of environment variables to set, and then to set them.

So far, I have this:

#!/bin/bash

cat $1 | while read kv
do
    key=${kv%=*}
    val=`echo ${kv#*=} | sed 's/^"\|"$//g'`
    export $key="$val"
done

And I want to read a file like this:

XAUTHLOCALHOSTNAME="localhost"
DISPLAY=":0"
XAUTHORITY="/tmp/some-XAuthority"

I only need these variables in scope for the duration of the script, so I don't need to solve the problem of setting a variable in script for the parent scope.

From my testing, I think my problem lies with export $key="$val", so I think I just need a replacement for that line.

Best Answer

export $key=$val should work just fine in bash. I suspect your issue is the pipe (|). All commands in a pipeline are executed in a subshell. In your example, the instance of bash that is running your script will fork off 2 subshells: one for cat $1 and another for the while read .... The variables are assigned and exported in the subshell running the while loop, and then all of them are promptly thrown out when the subshell exits. One way to fix this is to not spawn subshells at all. Instead of a useless use of cat, try redirection instead:

while read ...; do
   ...
done < "$1"

BashFAQ 24 explains this in much greater detail.

An alternate approach is to just source the file, with exports thrown in:

. <(sed '/^export/!s/^/export /' "$1")

The <( ) is process substitution.

As an additional note of caution, since you are reading environment variables from an argument, you must make sure that the argument file $1 is a trusted source so it can't do bad things to your script.

Related Question