2

I want to load some environment variables from a file before running a node script, so that the script has access to them. However, I don't want the environment variables to be set in my shell after the script is done executing.

I can load the environment variables like this:

export $(cat app-env-vars.txt | xargs) && node my-script.js

However, after the command is run, all of the environment variables are now set in my shell.

I'm asking this question to answer it, since I figured out a solution but couldn't find an answer on SO.

Cully
  • 6,427
  • 4
  • 36
  • 58
  • 2
    Use `dotenv` to run your script instead. – chepner Jun 05 '20 at 20:14
  • Something like `node -r dotenv/config my-script.js dotenv_config_path=app.env`, I believe. – chepner Jun 05 '20 at 20:15
  • @chepner Good suggestion. I'm aware of dotenv. I have good reasons for not using it in this case. Also, I wanted a generic solution to use beyond this specific example. – Cully Jun 05 '20 at 20:20
  • 1
    Keep in mind, though, that the rules for parsing a `.env` file are *not* the same as the shell's parsing rules. An example from https://github.com/motdotla/dotenv: a line like `FOO= some value` has to be written `FOO=" some value"` for the shell to process it correctly. The shell will also not expand `\n` to a newline. – chepner Jun 05 '20 at 20:25
  • @chepner Valuable note. Thanks. I wasn't really trying to target the `.env` format specifically; just meant it as a file containing shell-style environment variables. I renamed the file in the example accordingly. – Cully Jun 05 '20 at 21:08

3 Answers3

4

If you wrap the command in parentheses, the exports will be scoped to within those parens and won't pollute the global shell namespace:

(export $(cat app-env-vars.txt | xargs) && node my-script.js)

Echo'ing one of the environment variables from the app.env file after executing the command will show it as empty.

Cully
  • 6,427
  • 4
  • 36
  • 58
  • 1
    This will not work for all valid .env files, due to possible shell expansion of the unquoted command substitution and grammatical differences between shell assignments and the rules documented at https://github.com/motdotla/dotenv. – chepner Jun 05 '20 at 20:35
  • @chepner I wasn't really trying to target the `.env` format specifically; just meant it as a file containing shell-style environment variables. I renamed the file in the example accordingly. – Cully Jun 05 '20 at 21:08
  • It's still not going to work for very reasonable assignments like `FOO="x y z"`. The result will be `FOO=x` and exported-but-undefined variables `y` and `z`. – chepner Jun 05 '20 at 22:06
  • @chepner Noted. Thanks. This solves the immediate problem of not polluting my shell's namespace after execution. – Cully Jun 05 '20 at 23:12
4

This is what the env command is for:

env - run a program in a modified environment

You can try something like:

env $(cat app-en-vars.txt) node my-script.js

This (and any unquoted $(...) expansion) is subject to word splitting and glob expansion, both of which can easily cause problems with something like environment variables.

A safer approach is to use arrays, like so:

my_vars=(
  FOO=bar
  "BAZ=hello world"
  ...
)
env "${my_vars[@]}" node my-script.js

You can populate an array from a file if needed. Note you can also use -i with env to only pass the environment variables you set explicitly.


If you trust the .txt's files contents, and it contains valid Bash syntax, you should source it (and probably rename it to a .sh/.bash extension). Then you can use a subshell, as you posted in your answer, to prevent the sourced state from leaking into the parent shell:

( source app-env-vars.txt && node my-script.js )
dimo414
  • 47,227
  • 18
  • 148
  • 244
  • Really good to know about the env command. Though this solution doesn't handle quoted values (e..g `VARNAME="a b c"`). The eval solution that Diego posted seems to be ideal. – Cully Jun 06 '20 at 04:45
  • 1
    If the `.txt` file is actually a valid Bash file (and you trust it) you should `source` it. I've updated my answer. – dimo414 Jun 07 '20 at 00:48
0

If you file just contains variables like

FOO='x y z'
BAR='bar'
...

you can try

eval $(< app-en-vars.txt) node my-script.js
Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
  • Nice! This is a great solution. It works with quoted values. I like it. – Cully Jun 06 '20 at 00:02
  • 1
    This won't work for all sorts of plausible situations, such as Bash syntax in the `.txt` file. The use of `eval` permits arbitrary [shell injection exploits](https://en.wikipedia.org/wiki/Code_injection#Shell_injection). Even if you trust the contents of the `.txt` file, any improperly quoted contents could trigger unexpected behavior. – dimo414 Jun 07 '20 at 00:47
  • @dimo414 that's why I started specifying the condition when this is valid – Diego Torres Milano Jun 08 '20 at 21:36