Leo Jiang
Leo Jiang

Reputation: 26125

Is it possible to make the Bash "source" command keep environment variables passed in manually?

I have a file .env containing environment variables, e.g.:

TZ=UTC
NODE_ENV=development

Then, in my Bash scripts, I use:

set -o allexport; source .env; set +o allexport

This loads all the environment variables from .env.

However, if I manually pass environment variables to the script, they get overwritten by source. E.g. if I run NODE_ENV=production ./script.sh, I'd like it to keep NODE_ENV=production instead of using NODE_ENV=development from .env. How could I do this?

I'm using Ubuntu 20

Upvotes: 11

Views: 8838

Answers (4)

user1934428
user1934428

Reputation: 22247

You have to consider the following cases:

  1. A variable X is already defined, and the sourced file should then not touch it.

  2. A variable X is already defined, and the sourced file should then ensure that it is put into the environment.

  3. A variable X is not yet defined, and the sourced file then should create it.

  4. A variable X is not yet defined, and the sourced file then should create it and put it into the environment.

If you want 1+3, this is the simplest:

: ${X:=value}

The : is simply the bash no-op command and the := ensures that X gets the value if it is not defined yet. The export status (i.e. in environment or not in environment) is not affected.

If you want the combination 2+4, you could do either

export X=${X:-value}

or

: ${X:=value}
export X

The export statement ensures that the variable is in the environment afterwards, even if it was not in the environment before.

For my taste, the latter variant is more verbose, but clearer to read.

In theory you could also ask for the combination 1+3, i.e. not modify the environmental state of the variable, if it is defined already, but create it in the environment, if it is not defined. I can't think of a reasonable use case for this, but if you need, I can amend my answer and provide a solution for this as well.

Upvotes: 3

KamilCuk
KamilCuk

Reputation: 141235

Store environment "for later", source the file, then restore environment.

curenv=$(declare -p -x)
source .env
eval "$curenv"

Upvotes: 19

Antonio Petricca
Antonio Petricca

Reputation: 11036

You can't because the source command is invoked after passing external variables.

To accomplish that a workaround may require some coding:

  1. Don't use source command.
  2. Load .env file content and parse it into an associative array.
  3. For each variable it contains, check if it is already assigned (externally).
  4. If it not assigned, assign the value into the array.

Upvotes: 2

oguz ismail
oguz ismail

Reputation: 50775

I don't think it is. But you can modify your .env file as follows to prevent it from overwriting NODE_ENV.

TZ=UTC
NODE_ENV=${NODE_ENV:-development}

Upvotes: 5

Related Questions