72

Let's say I have .env file contains lines like below:

USERNAME=ABC
PASSWORD=PASS

Unlike the normal ones have export prefix so I cannot source the file directly.

What's the easiest way to create a shell script that loads content from .env file and set them as environment variables?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
James Lin
  • 25,028
  • 36
  • 133
  • 233
  • How are values with spaces defined? Is it `var="value with spaces"`, or `var=value with spaces`? The two call for quite different approaches. – Charles Duffy Apr 06 '17 at 23:58
  • 3
    Possible duplicate of [Set environment variables from file](https://stackoverflow.com/questions/19331497/set-environment-variables-from-file) – Nick Grealy Sep 28 '18 at 00:08
  • 2
    @NickGrealy, true, but there's an accepted answer that's badly wrong on that one (fails egregiously when values contain whitespace, and the stated workarounds are buggy) -- so if we were going to do duplicate linkage, I'd be going in the other direction. – Charles Duffy Sep 24 '22 at 15:49

7 Answers7

133

If your lines are valid, trusted shell but for the export command

This requires appropriate shell quoting. It's thus appropriate if you would have a line like foo='bar baz', but not if that same line would be written foo=bar baz

set -a # automatically export all variables
source .env
set +a

If your lines are not valid shell

The below reads key/value pairs, and does not expect or honor shell quoting.

while IFS== read -r key value; do
  printf -v "$key" %s "$value" && export "$key"
done <.env
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • `source .env` basically does the needful. just ensure that you do not have spaces in your .env values. – Adépòjù Olúwáségun Dec 25 '19 at 20:15
  • 6
    @AdépòjùOlúwáségun, without the `set -a` or the `export`, values are set only for the local shell, but not for its subprocesses. The OP explicitly says "environment variables", not "shell variables". `foo=bar` does not set an environment shell variable, it sets a shell variable. – Charles Duffy Dec 25 '19 at 21:41
  • Okay, I guess my use case was different. thanks for the clarity – Adépòjù Olúwáségun Dec 26 '19 at 04:34
  • 1
    ```while IFS== read -r key value; do key=`echo $key | sed 's/ *$//g'`; value=`echo $value | sed 's/ *$//g'`; export "$key"="$value"; eval "echo $key = \${$key}"; done < .env ``` Made a small change to trim whitespaces – Rodrigo Torres Jul 08 '21 at 22:20
  • 1
    @RodrigoTorres, that code has quite a lot of bugs; I wouldn't advise using it at all. `echo $key` and `echo $value` are themselves buggy -- see [I just assigned a variable, but `echo $variable` shows something else!](https://stackoverflow.com/questions/29378566); if your value contains a `*` you could end up with a list of filenames. There are also considerably more efficient approaches available -- spinning up command substitutions and running external commands in them is expensive compared to stripping spaces with [parameter expansion](https://wiki.bash-hackers.org/syntax/pe). – Charles Duffy Oct 20 '21 at 13:17
  • Thanks @CharlesDuffy. I will try to apply those fixes to the code. – Rodrigo Torres Oct 26 '21 at 12:06
  • Where does the `.env` go in this answer? – j7skov Jul 12 '22 at 20:37
  • 1
    @j7skov, in the first section, it's the word after `source` (and was already present). In the second section, `<.env` goes after the `done` (and I've edited to add it there). – Charles Duffy Jul 12 '22 at 21:38
54

This will export everything in .env:

export $(xargs <.env)

Edit: this requires the environment values to not have whitespace. If this does not match your use case you can use the solution provided by Charles

Edit2: I recommend adding a function to your profile for this in any case so that you don't have to remember the details of set -a or how xargs works.

voutasaurus
  • 2,968
  • 2
  • 24
  • 37
  • 1
    Consider if your `.env` was created with the following code: `printf '%s\n' 'password="two words"' 'another=foo' >.env`. In that case, the arguments passed to `export` would be `password=two`, `words`, and `another=foo`; `words` would no longer be part of the `password`, and would be a separate argument on its own (so the command would be trying to export a preexisting variable with the name `words`). – Charles Duffy May 20 '18 at 21:39
  • 1
    Would `export $(xargs -L 1 <.env)` solve that case @CharlesDuffy ? – voutasaurus Jul 30 '19 at 23:48
  • I don't see how it could - the command substitution's output is still being word-split (and glob-expanded) before the final `export` command is assembled. – Charles Duffy Aug 30 '20 at 23:40
5

This is what I use:

load_dotenv(){
  # https://stackoverflow.com/a/66118031/134904
  # Note: you might need to replace "\s" with "[[:space:]]"
  source <("$1" | sed -e '/^#/d;/^\s*$/d' -e "s/'/'\\\''/g" -e "s/=\(.*\)/='\1'/g")
}

set -a
[ -f "test.env" ] && load_dotenv "test.env"
set +a

If you're using direnv, know that it already supports .env files out of the box :)

Add this to your .envrc:

[ -f "test.env" ] && dotenv "test.env"

Docs for direnv's stdlib: https://direnv.net/man/direnv-stdlib.1.html

kolypto
  • 31,774
  • 17
  • 105
  • 99
  • 1
    `cat $1 | ...` misbehaves with filenames containing spaces or glob characters (`cat -- "$1" | ...` would be better, or just `<"$1" ...` to be both more correct and more efficient), and `\s` in a sed expression is a nonportable extension (it should be written `[[:space:]]` to be compatible with standard POSIX sed). Also, see https://wiki.bash-hackers.org/scripting/obsolete re: the `function` keyword; it's better just `load_dotenv() {` with no preceding `function` – Charles Duffy Apr 13 '23 at 11:45
2

Found this:

http://www.commandlinefu.com/commands/view/12020/export-key-value-pairs-list-as-environment-variables

while read line; do export $line; done < <(cat input)

UPDATE So I've got it working as below:

#!/bin/sh
while read line; do export $line; done < .env
James Lin
  • 25,028
  • 36
  • 133
  • 233
  • 7
    `< <(cat input)` is a pretty complicated way to say `< input`, isn't it? – Chris Apr 06 '17 at 23:47
  • 1
    `export "$line"` is going to be a bit better-behaved, assuming it's one variable to a line but that the values are able to contain spaces. – Charles Duffy Apr 06 '17 at 23:57
  • 2
    That said, look at what this code does with `line="hello world"` -- you get the quotes exported as part of the literal data, rather than treated as syntax. That might be fine, but since the OP was saying the only reason they couldn't `source` the file was the lack of `export` prefixes, it very well might not. – Charles Duffy Apr 07 '17 at 00:01
  • Also, you'll want to add the argument `-r` to `read` so it doesn't mangle backslashes, and quote `"$line"` so its contents don't get string-split and glob-expanded before `export` sees them. – Charles Duffy Jul 19 '19 at 16:32
  • 1
    When you have it written as `export $line` instead of `export "$line"`, a line like `foo='bar=baz qux=meh'` will write one variable `foo` with the value `'bar=baz`, and a second variable `qux` with the value `meh'`. That's certainly not what the user wants/expects. – Charles Duffy Feb 03 '22 at 13:59
1

This script works perfectly for me (Jun 2023).

#!/bin/sh

# Load environment variables from .env file
[ ! -f .env ] || export $(grep -v '^#' .env | xargs)

Example of .env file:

# Database settings
DB_HOST=localhost
DB_PORT=5432
DB_NAME=mydatabase
DB_USER=myuser
DB_PASSWORD=mypassword

# API keys
API_KEY=abc123
SECRET_KEY=def456

# Other settings
DEBUG_MODE=true
LOG_LEVEL=info
luthfimasruri
  • 61
  • 2
  • 5
-1

use command below on ubuntu

$ export $(cat .env)
  • ^^ This is how this should be done if the file contains a single `var=value` pair. And if the file only contains a `value`, this can be written as: `export myvar=$(cat myfilepath)` – Sam Sirry Feb 13 '23 at 03:07
-1

Try comand below

export `source .env`