Set environment variables from file of key/value pairs
Asked Answered
F

50

1135

TL;DR: How do I export a set of key/value pairs from a text file into the shell environment?


For the record, below is the original version of the question, with examples.

I'm writing a script in bash which parses files with 3 variables in a certain folder, this is one of them:

MINIENTREGA_FECHALIMITE="2011-03-31"
MINIENTREGA_FICHEROS="informe.txt programa.c"
MINIENTREGA_DESTINO="./destino/entrega-prac1"

This file is stored in ./conf/prac1

My script minientrega.sh then parses the file using this code:

cat ./conf/$1 | while read line; do
    export $line
done

But when I execute minientrega.sh prac1 in the command line it doesn't set the environment variables

I also tried using source ./conf/$1 but the same problem still applies

Maybe there is some other way to do this, I just need to use the environment variables of the file I pass as the argument of my script.

Footcandle answered 12/10, 2013 at 7:1 Comment(7)
Same on unix: unix.stackexchange.com/questions/31797/…Glum
Same with Ruby: #2139580, a gem that does it: github.com/bkeepers/dotenvGlum
This is a great question but is phrased way too specifically, with particular variable names ("MINIENTREGA_FECHALIMITE"? what does that mean?) and numbers (3). The general question is simply, "How do I export a set of key/value pairs from a text file into the shell environment".Moriahmoriarty
Also, this has already been answered on unix.SE and is arguably more on-topic there.Moriahmoriarty
A tip probably useful for beginners: Make sure you'll "execute" the script when sourcing environment variables in it. That way, you won't let them enter and pollute your own environment and ALSO otherwise can even be unsecure sometimes, for example, when you have secrets stored in one of those environment variables.Lightweight
I have my reservation on marking this question as dupe of a question that appeared 4 years later.Aristotelianism
@anubhava, that's fair, but the answer quality is pretty atrocious here. The first and foremost goal above all others is to give people good information, no? (There are some good answers on this question, but they're low-scoring compared to the bug-filled one at the top; this is a place where Stack Overflow's format and rules are not serving our community/readers well).Xylidine
A
375

Problem with your approach is the export in the while loop is happening in a sub shell, and those variable will not be available in current shell (parent shell of while loop).

Add export command in the file itself:

export MINIENTREGA_FECHALIMITE="2011-03-31"
export MINIENTREGA_FICHEROS="informe.txt programa.c"
export MINIENTREGA_DESTINO="./destino/entrega-prac1"

Then you need to source in the file in current shell using:

. ./conf/prac1

OR

source ./conf/prac1
Aristotelianism answered 12/10, 2013 at 7:5 Comment(8)
And if it's not from a file, use < <(commands that generate output)Grider
You have a more clean solution, I have a preference for set -o allexport Hutchison
If using this .env file between systems, inserting export would break it for things like Java, SystemD, or other toolsIslek
awk '{print "export " $0}' envfile convenience command to prepend export to the beginning of every lineDoublethink
You don't need to prefix with export if you use . ./conf/prac1Vincenzovincible
While this does work, I've found that putting export at the beginning of every environmental variable messes with a lot of programs trying to parse them (I can't use the vs code debugger when everything is prefixed with export for example).Romy
@Vincenzovincible Yes you do. Without export this defines parameters, not environment variables, which is something fundamentally different.Forthwith
adding 1000 export keywords to the file and then instantly remove them is not fair.Minuet
S
1619

This might be helpful:

export $(cat .env | xargs) && rails c

Reason why I use this is if I want to test .env stuff in my rails console.

gabrielf came up with a good way to keep the variables local. This solves the potential problem when going from project to project.

env $(cat .env | xargs) rails

I've tested this with bash 3.2.51(1)-release


Update:

To ignore lines that start with #, use this (thanks to Pete's comment):

export $(grep -v '^#' .env | xargs)

And if you want to unset all of the variables defined in the file, use this:

unset $(grep -v '^#' .env | sed -E 's/(.*)=.*/\1/' | xargs)

Update:

To also handle values with spaces, use:

export $(grep -v '^#' .env | xargs -d '\n')

on GNU systems -- or:

export $(grep -v '^#' .env | xargs -0)

on BSD systems.


From this answer you can auto-detect the OS with this:

export-env.sh

#!/bin/sh

## Usage:
##   . ./export-env.sh ; $COMMAND
##   . ./export-env.sh ; echo ${MINIENTREGA_FECHALIMITE}

unamestr=$(uname)
if [ "$unamestr" = 'Linux' ]; then

  export $(grep -v '^#' .env | xargs -d '\n')

elif [ "$unamestr" = 'FreeBSD' ] || [ "$unamestr" = 'Darwin' ]; then

  export $(grep -v '^#' .env | xargs -0)

fi

Shoshanashoshanna answered 3/1, 2014 at 17:10 Comment(6)
Here's a shorter variation eval $(cat .env) railsAshkhabad
on my end this doesn't seem to work with multiple entries in the .env file because it doesn't preseve newlines, so you get a SOME_ENV_VAR=sdfajkasldfjlasdkfj ANOTHERVAR=sdafjasdjasdfkl all as a single var (because it's all after a single line export).Myxomycete
This is too much complicated - pls consider simpler way with set -o allexport belowHydrometer
In my cases a \r was added which caused wrong values. I added tr -d '\r' but tr '\r' '\0' will also work. Final command is export $(grep -v '^#' .env | tr '\r' '\0' | xargs -d '\n')Coloring
No, export $(grep -v '^#' .env | xargs -0) is not correct on GNU systems. The unquoted expansion still word-splits on spaces; the fact that xargs is emitting NULs doesn't change that. (And some versions of bash just silently delete NULs from command substitution results, so the exact behavior that code has is version-dependent and thus not reliably testable). And for that matter, xargs -d '\n' is unnecessary everywhere, because the newline character is present in IFS, so in command substitution results it's parsed exactly the same way as a space.Xylidine
@manalang: eval will not localize variables.Minuet
F
993

-o allexport enables all following variable definitions to be exported. +o allexport disables this feature.

set -o allexport
source conf-file
set +o allexport
Florez answered 21/6, 2015 at 22:3 Comment(3)
or the one-liner set -o allexport && source conf-file && set +o allexport. Thanks for the great share @user4040650.Michi
This works in a scriptAthens
This works. Can confirm even with $ special characters.Daven
A
375

Problem with your approach is the export in the while loop is happening in a sub shell, and those variable will not be available in current shell (parent shell of while loop).

Add export command in the file itself:

export MINIENTREGA_FECHALIMITE="2011-03-31"
export MINIENTREGA_FICHEROS="informe.txt programa.c"
export MINIENTREGA_DESTINO="./destino/entrega-prac1"

Then you need to source in the file in current shell using:

. ./conf/prac1

OR

source ./conf/prac1
Aristotelianism answered 12/10, 2013 at 7:5 Comment(8)
And if it's not from a file, use < <(commands that generate output)Grider
You have a more clean solution, I have a preference for set -o allexport Hutchison
If using this .env file between systems, inserting export would break it for things like Java, SystemD, or other toolsIslek
awk '{print "export " $0}' envfile convenience command to prepend export to the beginning of every lineDoublethink
You don't need to prefix with export if you use . ./conf/prac1Vincenzovincible
While this does work, I've found that putting export at the beginning of every environmental variable messes with a lot of programs trying to parse them (I can't use the vs code debugger when everything is prefixed with export for example).Romy
@Vincenzovincible Yes you do. Without export this defines parameters, not environment variables, which is something fundamentally different.Forthwith
adding 1000 export keywords to the file and then instantly remove them is not fair.Minuet
E
329
set -a
. ./env.txt
set +a

If env.txt is like:

VAR1=1
VAR2=2
VAR3=3
...

Explanations -a is equivalent to allexport. In other words, every variable assignment in the shell is exported into the environment (to be used by multiple child processes). More information can be found in the Set builtin documentation:

-a     Each variable or function that is created or modified is given the export attribute and marked for export to the environment of subsequent commands.

Using ‘+’ rather than ‘-’ causes these options to be turned off. The options can also be used upon invocation of the shell. The current set of options may be found in $-.

Espinoza answered 30/8, 2017 at 23:34 Comment(4)
what if there is a comment? and VAR2=$VAR1?Pipit
Hi @Alexis. The . command used here is essentially like typing on the terminal. You can try it yourself on the terminal and see what the results would be. Comments will be ignored and references to other variables will work so long as they have been defined earlier.Espinoza
Yes, I tried it was working that way. Thanks for the follow-up!Pipit
The most straightforward answer.Dacosta
B
113

I found the most efficient way is:

export $(xargs < .env)

Explanation

When we have a .env file like this:

key=val
foo=bar

run xargs < .env will get key=val foo=bar

so we will get an export key=val foo=bar and it's exactly what we need!

Limitation

  1. It doesn't handle cases where the values have spaces in them. Commands such as env produce this format. – @Shardj
Blindfold answered 26/2, 2020 at 4:30 Comment(1)
perfect solution for me.Violist
P
52

The allexport option is mentioned in a couple of other answers here, for which set -a is the shortcut. Sourcing the .env really is better than looping over lines and exporting because it allows for comments, blank lines, and even environment variables generated by commands. My .bashrc includes the following:

# .env loading in the shell
dotenv () {
  set -a
  [ -f .env ] && . .env
  set +a
}

# Run dotenv on login
dotenv

# Run dotenv on every new directory
cd () {
  builtin cd $@
  dotenv
}
Proserpina answered 4/12, 2015 at 16:57 Comment(2)
This looks nice, but you do you unload environment variables when you leave the directory?Billy
I don't unset variables, and it's never been a problem. My apps tend to use variable names that are distinct, and if there is overlap, I'll set them to blank in that .env with VAR=.Proserpina
T
43

The problem with source is that it requires the file to have a proper bash syntax, and some special characters will ruin it: =, ", ', <, >, and others. So in some cases you can just

source development.env

and it will work.

This version, however, withstands every special character in values:

set -a
source <(cat development.env | \
    sed -e '/^#/d;/^\s*$/d' -e "s/'/'\\\''/g" -e "s/=\(.*\)/='\1'/g")
set +a

Explanation:

  • -a means that every bash variable would become an environment variable
  • /^#/d removes comments (strings that start with #)
  • /^\s*$/d removes empty strings, including whitespace
  • "s/'/'\\\''/g" replaces every single quote with '\'', which is a trick sequence in bash to produce a quote :)
  • "s/=\(.*\)/='\1'/g" converts every a=b into a='b'

As a result, you are able to use special characters :)

To debug this code, replace source with cat and you'll see what this command produces.


Note for direnv users: it has a helper function dotenv, use it instead in your .envrc file:

[ -f ".env" ] && dotenv ".env"
Toastmaster answered 9/2, 2021 at 11:21 Comment(4)
Works for me (TM) on bash, using the following annoying string: FOO=~`#$&*()\|[=]{}; '"<>/?!Teleology
This almost worked for me, but had to swap out \s with [[:space:]] to make it also work on bash on FreeBSD/Mac: ``` source <(cat .env | sed -e '/^#/d;/^[[:space:]]*$/d' -e "s/'/'\\\''/g" -e "s/=(.*)/='\1'/g") ```Theurer
Worked for me, although I had to replace the last sed with: sed -e '/^#/d;/^\s*$/d' -e "s/'/'\\\''/g" -e "s/\ *=\ */=/g") to escape any spaces around the equal signsTetrad
Perfect little nugget to add to your dotfiles - worked a treat when my .env had semicolons in it! 🙏🏼Shulock
B
34
eval $(cat .env | sed 's/^/export /')
Broadtail answered 7/4, 2015 at 9:49 Comment(5)
Using eval $(cat .env | sed 's/^[^$]/export /') allows you to have empty lines for better readability.Hughey
I find that cat .env | sed 's/^[^$]/export /' strips off the initial character. I.e. for a file A=foo\nB=bar\n I get export =foo\nexport =bar\n. This works better for skipping blank lines: cat .env | sed '/^$/! s/^/export /'.Cecilia
(I also note for the sake of UNIX code golfers that you don't need cat in either case: eval $(sed 's/^/export /' .env) works just as well.)Cecilia
dont'support commented row initial with #Laugh
eval sed 's/^/export /' .envLunula
E
27

Here is another sed solution, which does not run eval or require ruby:

source <(sed -E -n 's/[^#]+/export &/ p' ~/.env)

This adds export, keeping comments on lines starting with a comment.

.env contents

A=1
#B=2

sample run

$ sed -E -n 's/[^#]+/export &/ p' ~/.env
export A=1
#export B=2

I found this especially useful when constructing such a file for loading in a systemd unit file, with EnvironmentFile.

Easterling answered 6/4, 2016 at 16:21 Comment(0)
S
24

Not exactly sure why, or what I missed, but after running trough most of the answers and failing. I realized that with this .env file:

MY_VAR="hello there!"
MY_OTHER_VAR=123

I could simply do this:

source .env
echo $MY_VAR

Outputs: Hello there!

Seems to work just fine in Ubuntu linux.

Stay answered 6/8, 2020 at 6:33 Comment(4)
If you use such and env file with Docker, your MY_VAR will contain quotes as part of the value :) docs.docker.com/compose/env-fileToastmaster
@Toastmaster The same would happen with any other command of the higher voted answers. It is just the choice of the example. This is just to show that you can also just source it - is the core idea. The rest of the tricks is for example to cover special signs as well.Clairvoyance
Works on mac as well.Rarity
What you're missing is that this does not define any environment variables. It defines parameters. They're completely different: no command you invoke will see these values because parameters are not inherited.Forthwith
L
22

The shortest way I found:

Your .env file:

VARIABLE_NAME="A_VALUE"

Then just

. ./.env && echo ${VARIABLE_NAME}

Bonus: Because it's a short one-liner, it's very useful in package.json file

  "scripts": {
    "echo:variable": ". ./.env && echo ${VARIABLE_NAME}"
  }

Note: This way does not export the variables to the child process, check other answers if this is your need.

Lamrert answered 28/5, 2019 at 10:56 Comment(6)
How about if you have a lot of variables?Smoking
@Madeo you can add as many lines as you want, the same way as the line VARIABLE_NAME="A_VALUE"Lamrert
Warning: this exports these variables into your active shell...Habit
yes, . is a synonym for source and therefore does the sameLamrert
This does not define environment variables, which is what this question is about.Forthwith
@KonradRudolph yes that's because the variables are not exported to the child process, as collin already commented. This is not how I first understood the question and there are other longer solutions available here. Just pick the one suiting your needs. I commented my answer to clarify this point.Lamrert
A
21

I have upvoted user4040650's answer because it's both simple, and it allows comments in the file (i.e. lines starting with #), which is highly desirable for me, as comments explaining the variables can be added. Just rewriting in the context of the original question.

If the script is callled as indicated: minientrega.sh prac1, then minientrega.sh could have:

set -a # export all variables created next
source $1
set +a # stop exporting

# test that it works
echo "Ficheros: $MINIENTREGA_FICHEROS"

The following was extracted from the set documentation:

This builtin is so complicated that it deserves its own section. set allows you to change the values of shell options and set the positional parameters, or to display the names and values of shell variables.

set [--abefhkmnptuvxBCEHPT] [-o option-name] [argument …] set [+abefhkmnptuvxBCEHPT] [+o option-name] [argument …]

If no options or arguments are supplied, set displays the names and values of all shell variables and functions, sorted according to the current locale, in a format that may be reused as input for setting or resetting the currently-set variables. Read-only variables cannot be reset. In POSIX mode, only shell variables are listed.

When options are supplied, they set or unset shell attributes. Options, if specified, have the following meanings:

-a Each variable or function that is created or modified is given the export attribute and marked for export to the environment of subsequent commands.

And this as well:

Using ‘+’ rather than ‘-’ causes these options to be turned off. The options can also be used upon invocation of the shell. The current set of options may be found in $-.

Azaleah answered 8/3, 2017 at 11:46 Comment(0)
C
17

SAVE=$(set +o | grep allexport) && set -o allexport && . .env; eval "$SAVE"

This will save/restore your original options, whatever they may be.

Using set -o allexport has the advantage of properly skipping comments without a regex.

set +o by itself outputs all your current options in a format that bash can later execute. Also handy: set -o by itself, outputs all your current options in human-friendly format.

Calvillo answered 9/9, 2015 at 20:57 Comment(1)
I would probably exec env -i bash to clear the existing environment before calling eval if you need to unset variables that are only set within .env.Favored
M
17

Improving on Silas Paul's answer

exporting the variables on a subshell makes them local to the command.

(export $(cat .env | xargs) && rails c)

Mestizo answered 21/10, 2015 at 20:37 Comment(1)
Then you can use this (set -a; source dev.env; set +a; rails c) to also have the benefits of sourcing (e.g. script can execute).Shirring
M
15

Here's my variant:

  with_env() {
    (set -a && . ./.env && "$@")
  }

compared with the previous solutions:

  • it does not leak variables outside scope (values from .env are not exposed to caller)
  • does not clobber set options
  • returns exit code of the executed command
  • uses posix compatible set -a
  • uses . instead of source to avoid bashism
  • command is not invoked if .env loading fails
with_env rails console
Maxama answered 22/1, 2020 at 14:24 Comment(1)
You can also run inline (the variables are exposed to your current terminal session): set -a && . ./.env && "$@" && echo "your comand here"Wolsky
I
14

If env supports the -S option one may use newlines or escape characters like \n or \t (see env):

env -S "$(cat .env)" command

.env file example:

KEY="value with space\nnewline\ttab\tand
multiple
lines"

Test:

env -S "$(cat .env)" sh -c 'echo "$KEY"'
Iambus answered 31/12, 2020 at 10:9 Comment(0)
B
14

Use shdotenv

dotenv support for shell and POSIX-compliant .env syntax specification
https://github.com/ko1nksm/shdotenv

eval "$(shdotenv)"

Usage

Usage: shdotenv [OPTION]... [--] [COMMAND [ARG]...]

  -d, --dialect DIALECT  Specify the .env dialect [default: posix]
                           (posix, ruby, node, python, php, go, rust, docker)
  -s, --shell SHELL      Output in the specified shell format [default: posix]
                           (posix, fish)
  -e, --env ENV_PATH     Location of the .env file [default: .env]
                           Multiple -e options are allowed
  -o, --overload         Overload predefined environment variables
  -n, --noexport         Do not export keys without export prefix
  -g, --grep PATTERN     Output only those that match the regexp pattern
  -k, --keyonly          Output only variable names
  -q, --quiet            Suppress all output
  -v, --version          Show the version and exit
  -h, --help             Show this message and exit

Requirements

shdotenv is a single file shell script with embedded awk script.

  • POSIX shell (dash, bash, ksh, zsh, etc)
  • awk (gawk, nawk, mawk, busybox awk)
Benedick answered 2/5, 2021 at 14:56 Comment(3)
Awesome tool and great attention to details. Thanks!!Worn
@PierreGramme Using a dedicated tool running at least two forks for resolving a problem of understanding a concept reduced in one command seem a little overkill!Grearson
@FHauri Maybe an overkill, but this question has 43 different answers: was it really such a simple problem? In my use case I have a .env file written in Python dialect and apply it to Bash. Can't simply use source due to different conventions for managing spaces etc. That tool and its analysis of differences was definitely useful for meWorn
O
12

Sorry to add yet another answer but because it's simplistic and works in many cases, try:

export $(< ~/my/.env)
Oxidimetry answered 13/1, 2023 at 14:6 Comment(0)
G
11

Simpler:

  1. grab the content of the file
  2. remove any blank lines (just incase you separated some stuff)
  3. remove any comments (just incase you added some...)
  4. add export to all the lines
  5. eval the whole thing

eval $(cat .env | sed -e /^$/d -e /^#/d -e 's/^/export /')

Another option (you don't have to run eval (thanks to @Jaydeep)):

export $(cat .env | sed -e /^$/d -e /^#/d | xargs)

Lastly, if you want to make your life REALLY easy, add this to your ~/.bash_profile:

function source_envfile() { export $(cat $1 | sed -e /^$/d -e /^#/d | xargs); }

(MAKE SURE YOU RELOAD YOUR BASH SETTINGS!!! source ~/.bash_profile or.. just make a new tab/window and problem solved) you call it like this: source_envfile .env

Graig answered 13/9, 2017 at 14:5 Comment(1)
I had to read .env text from gitlab secret variable for a pipeline: Based on your solution this command worked for me: source <( echo $(sed -E -n 's/[^#]+/ &/ p' <(echo "${2}" | tr -d '\r')) );. Somehow gitlab saves the secret variable with a windows carriage return, so I had to trim that with tr -d '\r'.Geomorphology
K
8

You can use your original script to set the variables, but you need to call it the following way (with stand-alone dot):

. ./minientrega.sh

Also there might be an issue with cat | while read approach. I would recommend to use the approach while read line; do .... done < $FILE.

Here is a working example:

> cat test.conf
VARIABLE_TMP1=some_value

> cat run_test.sh
#/bin/bash
while read line; do export "$line";
done < test.conf
echo "done"

> . ./run_test.sh
done

> echo $VARIABLE_TMP1
some_value
Kattegat answered 17/2, 2014 at 14:29 Comment(1)
Unlike most other answers, this solution doesn't eval test.conf as a script file. That makes it better. It's safer to not allow scripting unless you actually need it, especially if someone don't realize that's what's going on (or forgets).Suspect
R
8

I work with docker-compose and .env files on Mac, and wanted to import the .env into my bash shell (for testing), and the "best" answer here was tripping up on the following variable:

.env

NODE_ARGS=--expose-gc --max_old_space_size=2048

Solution

So I ended up using eval, and wrapping my env var defs in single quotes.

eval $(grep -v -e '^#' .env | xargs -I {} echo export \'{}\')

Bash Version

$ /bin/bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.
Rubdown answered 20/5, 2019 at 22:39 Comment(0)
M
8
t=$(mktemp) && export -p > "$t" && set -a && . ./.env && set +a && . "$t" && rm "$t" && unset t

How it works

  1. Create temp file.
  2. Write all current environment variables values to the temp file.
  3. Enable exporting of all declared variables in the sources script to the environment.
  4. Read .env file. All variables will be exported into current environment.
  5. Disable exporting of all declared variables in the sources script to the environment.
  6. Read the contents of the temp file. Every line would have declare -x VAR="val" that would export each of the variables into environment.
  7. Remove temp file.
  8. Unset the variable holding temp file name.

Features

  • Preserves values of the variables already set in the environment
  • .env can have comments
  • .env can have empty lines
  • .env does not require special header or footer like in the other answers (set -a and set +a)
  • .env does not require to have export for every value
  • one-liner
Miran answered 20/1, 2020 at 22:17 Comment(1)
I really appreciate your solution. In most projects you have .env files with comments, spaces, no export statement etc. Pretty nice!Brandebrandea
H
6

Building on other answers, here is a way to export only a subset of lines in a file, including values with spaces like PREFIX_ONE="a word":

set -a
. <(grep '^[ ]*PREFIX_' conf-file)
set +a
Hanahanae answered 9/3, 2017 at 0:44 Comment(0)
P
6

My requirements were:

  • simple .env file without export prefixes (for compatibility with dotenv)
  • supporting values in quotes: TEXT="alpha bravo charlie"
  • supporting comments prefixed with # and empty lines
  • universal for both mac/BSD and linux/GNU

Full working version compiled from the answers above:

  set -o allexport
  eval $(grep -v '^#' .env | sed 's/^/export /')
  set +o allexport
Pituri answered 18/4, 2019 at 10:13 Comment(1)
what's the point of "-o allexport" if you prepend them with "export" anyway?Hartnett
D
5

I have issues with the earlier suggested solutions:

  • @anubhava's solution makes writing bash friendly configuration files very annoying very fast, and also - you may not want to always export your configuration.
  • @Silas Paul solution breaks when you have variables that have spaces or other characters that work well in quoted values, but $() makes a mess out of.

Here is my solution, which is still pretty terrible IMO - and doesn't solve the "export only to one child" problem addressed by Silas (though you can probably run it in a sub-shell to limit the scope):

source .conf-file
export $(cut -d= -f1 < .conf-file)
Dysthymia answered 28/1, 2015 at 9:53 Comment(0)
M
5

My .env:

#!/bin/bash
set -a # export all variables

#comments as usual, this is a bash script
USER=foo
PASS=bar

set +a #stop exporting variables

Invoking:

source .env; echo $USER; echo $PASS

Reference https://unix.stackexchange.com/questions/79068/how-to-export-variables-that-are-set-all-at-once

Multilateral answered 26/5, 2017 at 0:28 Comment(0)
P
5

Here's my take on this. I had the following requirements:

  • Ignore commented lines
  • Allow spaces in the value
  • Allow empty lines
  • Ability to pass a custom env file while defaulting to .env
  • Allow exporting as well as running commands inline
  • Exit if env file doesn't exist
source_env() {
  env=${1:-.env}
  [ ! -f "${env}" ] && { echo "Env file ${env} doesn't exist"; return 1; }
  eval $(sed -e '/^\s*$/d' -e '/^\s*#/d' -e 's/=/="/' -e 's/$/"/' -e 's/^/export /' "${env}")
}

Usage after saving the function to your .bash_profile or equivalent:

source_env                # load default .env file
source_env .env.dev       # load custom .env file
(source_env && COMMAND)   # run command without saving vars to environment

Inspired by Javier and some of the other comments.

Phytopathology answered 10/8, 2020 at 12:48 Comment(0)
T
5

My version :

I print the file, remove commented lines, emptylines, and I split key/value from "=" sign. Then I just apply the export command.

The advantage of this solution is the file can contain special chars in values, like pipes, html tags, etc., and the value doesn't have to be surrounded by quotes, like a real properties file.

# Single line version
cat myenvfile.properties | grep -v '^#' | grep '=' | while read line; do IFS=\= read k v <<< $line; export $k="$v"; done

# Mutliline version:
cat myenvfile.properties | grep -v '^#' | grep '=' | while read line; do 
  IFS=\= read k v <<< $line
  export $k="$v"
done

Thermotherapy answered 22/10, 2021 at 7:43 Comment(0)
C
5

Some notes:

  1. The ".env" file should have an "LF" end-of-line sequence.
  2. Avoid using dynamic values in environment variables, such as variable1=$variable2@$variable3
  3. Avoid using a quotation (") in environment variables vavlue, such as variable="value"

This is the best and shortest answer

source .env && export $(cut -d= -f1 < .env)
Camaraderie answered 26/4, 2023 at 14:46 Comment(0)
P
4

White spaces in the value

There are many great answers here, but I found them all lacking support for white space in the value:

DATABASE_CLIENT_HOST=host db-name db-user 0.0.0.0/0 md5

I have found 2 solutions that work whith such values with support for empty lines and comments.

One based on sed and @javier-buzzi answer:

source <(sed -e /^$/d -e /^#/d -e 's/.*/declare -x "&"/g' .env)

And one with read line in a loop based on @john1024 answer

while read -r line; do declare -x "$line"; done < <(egrep -v "(^#|^\s|^$)" .env)

The key here is in using declare -x and putting line in double quotes. I don't know why but when you reformat the loop code to multiple lines it won't work — I'm no bash programmer, I just gobbled together these, it's still magic to me :)

Politician answered 5/4, 2018 at 14:20 Comment(3)
I had to modify the sed solution to get it to work. But first some explanation: -e is short for --expression, which just tells sed what operations to take. -e /^$/d deletes the empty lines from the output (not the file). -e /^#/d deletes the bash comments (lines that start with #) from the output. 's/.*/declare -x "&"/g' replaces (substitutes) the remaining lines with declare -x "ENV_VAR="VALUE"". When you source this, at least for me, it didn't work. Instead, I had to use source <(sed -e /^$/d -e /^#/d -e 's/.*/declare -x &/g' .env), to remove the extra " wrapper.Coinsure
I don't use ENV_VAR="lorem ipsum", I have ENV_VAR=lorem ipsum, without quotes in the .env file. Now I'm not sure why, but this was probably problematic in other tools that parse this file. And instead of lorem ipsum I have ended with "lorem ipsum" value – with quotes. Thx for the explanations :)Politician
If it was my choice, I wouldn't use ENV_VAR="lorem ipsum" either. In my use case, my hosting provider generates this file based on some configuration options I have set, and they insert the double quotes. So, I am forced to work around it. Thanks for your help here - saved me a lot of time trying to work out the correct sed options myself!Coinsure
S
4

First, create an environment file that will have all the key-value pair of the environments like below and named it whatever you like in my case its env_var.env

MINIENTREGA_FECHALIMITE="2011-03-31"
MINIENTREGA_FICHEROS="informe.txt programa.c"
MINIENTREGA_DESTINO="./destino/entrega-prac1"

Then create a script that will export all the environment variables for the python environment like below and name it like export_env.sh

#!/usr/bin/env bash

ENV_FILE="$1"
CMD=${@:2}

set -o allexport
source $ENV_FILE
set +o allexport

$CMD

This script will take the first argument as the environment file then export all the environment variable in that file and then run the command after that.

USAGE:

./export_env.sh env_var.env python app.py
Shipe answered 20/5, 2019 at 7:3 Comment(0)
S
4

Modified from @Dan Kowalczyk

I put this in ~/.bashrc.

set -a
. ./.env >/dev/null 2>&1
set +a

Cross-compatible very well with Oh-my-Zsh's dotenv plugin. (There is Oh-my-bash, but it doesn't have dotenv plugin.)

Sauer answered 21/8, 2020 at 2:18 Comment(0)
A
4

I use this:

source <(cat .env \
  | sed -E '/^\s*#.*/d' \
  | tr '\n' '\000' \
  | sed -z -E 's/^([^=]+)=(.*)/\1\x0\2/g' \
  | xargs -0 -n2 bash -c 'printf "export %s=%q;\n" "${@}"' /dev/null)

First Removing comments:

sed -E '/^\s*#.*/d'

Then converting to null delimiters instead of newline:

tr '\n' '\000'

Then replacing equal with null:

sed -z -E 's/^([^=]+)=(.*)/\1\x0\2/g'

Then printing pairs to valid quoted bash exports (using bash printf for %q):

xargs -0 -n2 bash -c 'printf "export %s=%q;\n" "${@}"' /dev/null

Then finally sourcing all of that.

It should work for just about all cases with all special characters.

Artieartifact answered 15/9, 2020 at 13:18 Comment(0)
G
3

How I save variables :

printenv | sed 's/\([a-zA-Z0-9_]*\)=\(.*\)/export \1="\2"/' > myvariables.sh

How I load them

source myvariables.sh
Golter answered 3/12, 2020 at 10:48 Comment(0)
T
3
sh -ac '. conf-file; yourcommand'

The -a switch exports all variables, so that they are available to the program.

Unlike the longer version set -a; . conf-file; set +a; yourcommand using sh ensures the exported values are not permanently polluting the current environment. It sources and exports the variables just for the program run in a subshell.

Turnabout answered 24/12, 2021 at 8:52 Comment(0)
G
2

export is the answer!

Interactive exercises by yourself

As is interactive, you could try near everything inline!

$ mkdir conf && printf 'MINIENTREGA_%s="%s"\n' FECHALIMITE 2011-03-31 FICHEROS \
    "informe.txt programa.c" DESTINO ./destino/entrega-prac1 >conf/prac1 

$ set -- prac1      # set "prac1" as positional argument "$1" see `help set`
$ while read -r line; do export $line; done <"conf/$1"
bash: export: `programa.c"': not a valid identifier
$ while read -r line; do LANG=C export "$line"; done <"conf/$1"
$ echo "$MINIENTREGA_FICHEROS"
"informe.txt programa.c"

Note the double quotes enclosing $line on export command!

source alias .

$ set -- prac1
$ . "conf/$1"
$ echo "$MINIENTREGA_FICHEROS"
informe.txt programa.c

Ok! Then now what's about export?

export command tell shell to export his variables to environment... So you have to export script variables before use them is any subprocess (like ruby, python, perl or even another shell script.

Cleaning previous operations for further demos

$ declare +x MINIENTREGA_FECHALIMITE MINIENTREGA_FICHEROS MINIENTREGA_DESTINO
$ unset MINIENTREGA_FECHALIMITE MINIENTREGA_FICHEROS MINIENTREGA_DESTINO

So from an interactive shell, simpliest way to try this is to run another (sub) :

$ set -- prac1
$ . "conf/$1"
$ sh -c 'echo "$MINIENTREGA_FICHEROS"'
 

This just print an empty line, but if you export your variables:  

$ export MINIENTREGA_FECHALIMITE MINIENTREGA_FICHEROS MINIENTREGA_DESTINO
$ sh -c 'echo "$MINIENTREGA_FICHEROS"'
informe.txt programa.c

Sample wrapper for exporting variables

Minimal wrapper, without security concern (care when sourcing script editable by other users!!).

#!/bin/sh

while IFS== read -r varname _;do
    case $varname in
         *[!A-Za-z0-9_]* | '' ) ;;
         * ) export $varname ;;
    esac
done <conf/$1
. conf/$1

busybox sh -c 'set | grep MINIENTREGA'

Run with prac1 as argument, should produce:

MINIENTREGA_DESTINO='./destino/entrega-prac1'
MINIENTREGA_FECHALIMITE='2011-03-31'
MINIENTREGA_FICHEROS='informe.txt programa.c'

In fine

  • Sourcing your config file is same than declaring variables.

  • Exporting your variables is an instruct to shell to share his variables in global environment for any subprocess.

This two operation can be done in any order indifferently. The only requirement is that both operations are done before you try to run any subprocess.

You could even do both operation together, by exporting in your config file, for sample:

export MINIENTREGA_FECHALIMITE="2011-03-31"
export MINIENTREGA_FICHEROS="informe.txt programa.c"
export MINIENTREGA_DESTINO="./destino/entrega-prac1"

You even could write this in one operation:

export MINIENTREGA_FICHEROS="informe.txt programa.c" \
  MINIENTREGA_FECHALIMITE="2011-03-31" MINIENTREGA_DESTINO="./destino/entrega-prac1"

Some bashisms now

do offer a lot of tools for variable manipulations

Playing with variable names: nameref

#!/bin/bash

buildMiniEntragaVar() {
    local -n _MiniEntragaVar="MINIENTREGA_$1"
    export "MINIENTREGA_$1"
    _MiniEntragaVar="$2"
}
buildMiniEntragaVar FECHALIMITE  "2011-03-31"
buildMiniEntragaVar FICHEROS     "informe.txt programa.c"
buildMiniEntragaVar DESTINO      "./destino/entrega-prac1"

Then, using , you could show attribute and content of any variable by using declare -p:

declare -p MINIENTREGA_FECHALIMITE MINIENTREGA_DESTINO MINIENTREGA_FICHEROS
declare -x MINIENTREGA_FECHALIMITE="2011-03-31"
declare -x MINIENTREGA_DESTINO="./destino/entrega-prac1"
declare -x MINIENTREGA_FICHEROS="informe.txt programa.c"

Expanding by variable name prefix matching:

This is a nice tool! From man page:

   ${!prefix*}
   ${!prefix@}
          Names matching prefix.  Expands to the names of variables  whose
          names begin with prefix, separated by the first character of the
          IFS special variable.  When @ is used and the expansion  appears
          within  double  quotes, each variable name expands to a separate
          word.

In practice last previous sample, using declare -p could be written:

declare -p ${!MINIENTREGA_*}
declare -x MINIENTREGA_DESTINO="./destino/entrega-prac1"
declare -x MINIENTREGA_FECHALIMITE="2011-03-31"
declare -x MINIENTREGA_FICHEROS="informe.txt programa.c"

Of course, this could be used to export previously defined variables:

MINIENTREGA_FECHALIMITE="2011-03-31"
MINIENTREGA_FICHEROS="informe.txt programa.c"
MINIENTREGA_DESTINO="./destino/entrega-prac1"

export "${!MINIENTREGA_@}"

Unexporting a variable:

Unfortunely, there is no unexport command. for this, you could use either flag -n of export command or sign + instead of - for declare command:

export -n MINIENTREGA_DESTINO
declare +x MINIENTREGA_FICHEROS

both commands will unset exportation flog of the variable.

Exporting array or associative array:

As array and associative array are kind of bashisms, they couldn't be exported at POSIX environment:

export -a myavar='(1 2)'
export -A myAvar='([foo]=1 [bar]=2)'
echo ${myavar[1]} ${myAvar[bar]}
2 2
declare -p "${!my@}"
declare -Ax myAvar=([foo]="1" [bar]="2" )
declare -ax myavar=([0]="1" [1]="2")

But

bash -c 'echo ${myavar[1]} ${myAvar[bar]}'
 

This just print an empty line!! While flag x was present!

For this, a kind of workaround could be to use --rcfile with -i flags:

bash --rcfile <(declare -p "${!my@}") --noediting -ic 'echo ${myavar[1]} ${myAvar[bar]}'
2 2
bash --rcfile <(declare -p "${!my@}") --noediting -i file.sh

Or if using interactive could cause issue, you could concatenate your script:

bash -c "$(declare -p "${!my@}");"'echo ${myavar[1]} ${myAvar[bar]}'
2 2

or

bash -c "$(declare -p "${!my@}");file.sh"

or even

bash -c "$(declare -p "${!my@}");. file.sh"

if file is not executable, like for sample:

bash -c "$(declare -p "${!my@}");. <(echo 'echo ${myavar[1]} ${myAvar[bar]}')"
2 2
Grearson answered 8/1, 2022 at 15:11 Comment(0)
S
2

I ended with a solution based on allexport + source approach. The main idea here is to prevent override of existent variables.

function load_env_file() {
    local FILE_PATH="${1}"
    local EXISTENT_VARS=$(declare)

    set -o allexport
    source "${FILE_PATH}"
    set +o allexport

    # errors are supressed as "declare" returns also readonly vars which are not overridable
    eval "${EXISTENT_VARS}" 2> /dev/null || true
}

# Usage example:
load_env_file "path/to/.env"
Sanfordsanfourd answered 20/3, 2022 at 21:46 Comment(0)
G
2

My take if you want leave the global environment variable space untouched, which I see as desirable.

Create a script like this:

# !/bin/sh
set -o allexport
source $1
set +o allexport
shift
exec $@

then use like this:

dotenv env-file my-binary
Glycol answered 3/9, 2022 at 5:11 Comment(0)
O
2

I build this script to source env vars dynamicaly.

I use this script because I don't want to have to remember the names of each of the variables I use in the project and I dont want the export command being stored in history or the full .env file exported into git.

#!/bin/sh

filename=".secret"

secret_var () {
    # Parametter 1 : Environnement vars anme

    bash -c 'read -p '$1=' -s voila && echo '$1'"=${voila}" > '$filename''
    export `cat .secret`
    rm $filename
    echo ''
}

public_var () {
    # Parametter 1 : Environnement vars anme

    bash -c 'read -p '$1=' voila && echo '$1'"=${voila}" > '$filename''
    export `cat .secret`
    rm $filename
}

if [ -e $filename ]
then
    echo "A file named '.secret' already exist. Remove it or edit this script."
else
    public_var MY_USER_VAR
    secret_var MY_PASS_VAR
fi

It's verry easy to use :

# To add var MY_VAR_NAME to the env
public_var MY_VAR_NAME
# To add var MY_VAR_NAME secretly into the env
secret_var MY_VAR_NAME

example :

callmarl@LAPTOP ~ % source set_env.sh
MY_USER_VAR=myusername
MY_PASS_VAR=
callmarl@LAPTOP ~ % env
MY_USER_VAR=myusername
MY_PASS_VAR=mysecretpass

You can of course use directly export instead public_var if you want the value being stored.

Orian answered 5/10, 2022 at 17:29 Comment(0)
P
1

I came across this thread when I was trying reuse Docker --env-files in a shell. Their format is not bash compatible but it is simple: name=value, no quoting, no substitution. They also ignore blank lines and # comments.

I couldn't quite get it posix compatible, but here's one that should work in bash-like shells (tested in zsh on OSX 10.12.5 and bash on Ubuntu 14.04):

while read -r l; do export "$(sed 's/=.*$//' <<<$l)"="$(sed -E 's/^[^=]+=//' <<<$l)"; done < <(grep -E -v '^\s*(#|$)' your-env-file)

It will not handle three cases in the example from the docs linked above:

  • bash: export: `123qwe=bar': not a valid identifier
  • bash: export: `org.spring.config=something': not a valid identifier
  • and it will not handle the passthrough syntax (a bare FOO)
Pennsylvania answered 23/6, 2017 at 20:55 Comment(0)
P
1

try something like this

for line in `cat your_env_file`; do if [[ $line != \#* ]];then export $line; fi;done
Purse answered 26/11, 2019 at 12:55 Comment(0)
D
1

My .env file looks like:

DATABASE_URI="postgres://sa:***@localhost:5432/my_db"
VARIABLE_1="SOME_VALUE"
VALIABLE_2="123456788"

Using the @henke's ways, the exported value ends up containing the quotation marks "

"postgres://sa:***@localhost:5432/my_db"
"SOME_VALUE"
"123456788"

But I want the exported value to contain only:

postgres://sa:***@localhost:5432/my_db
SOME_VALUE
123456788

To fix it, I edit the command to delete the quotation marks:

export $(grep -v '^#' dev.env | tr --delete '"' | xargs -d '\n')
Desexualize answered 19/12, 2019 at 13:44 Comment(0)
S
1

A POSIX-compliant solution (doesn't depend on bash)

As others have noted, the problem with using a for/while loop here, is that variables are not shared between a shell and its subshells. What we can do, however - is to pass text between shells using args/stdin/stdout.

Setting environment variables in a subshell is not going to help when we source the script

Variables are not going to propagate back up - but we know that we can send text back. And this text could also be code, which we can evaluate in the current shell using eval.

What if we instead generate code for setting all the environment variables, and then eval the result?

create_exports_script() {
    echo "$1" | while read line; do
        echo "export $line"
    done
}

file_contents=$(cat "./conf/myconf.env")
eval $(create_exports_script "$file_contents")

This kind of functional meta-programming in bash can be incredibly flexible. You can also generate other languages than bash/sh this way.

Svensen answered 1/9, 2021 at 23:19 Comment(0)
Q
0

If you're getting an error because one of your variables contains a value that contains white spaces you can try to reset bash's IFS (Internal Field Separator) to \n to let bash interpret cat .env result as a list of parameters for the env executable.

Example:

IFS=$'\n'; env $(cat .env) rails c

See also:

Quicktempered answered 20/12, 2015 at 20:23 Comment(0)
I
0

This one copes with spaces on the RHS, and skips 'weird' vars such as bash module definitions (with '()' in them):

echo "# source this to set env vars" > $bld_dir/.env
env | while read line; do
    lhs="${line%%=*}"
    rhs="${line#*=}"
    if [[ "$lhs" =~ ^[0-9A-Za-z_]+$ ]]; then
        echo "export $lhs=\"$rhs\"" >> $bld_dir/.env
    fi
done
Invisible answered 18/4, 2020 at 17:31 Comment(0)
P
0

If you have an intention to have exec as the last command of your script, you have an additional option by using execlineb interpreter. This is how the last line of your script would look like:

#!/bin/sh
...
exec envfile -I /etc/default/bla envfile /etc/default/bla-bla my_cmd

envfile ... are commands from execline suite and they rely on "chain loading". BTW, once you down this rabbit hole you may discover that you don't need shell anymore (... and reconsider your other life choices :-) It is quite useful for starting services with minimum overhead by using execlineb interpreter instead of shell entirely, i.e.:

#!/bin/execlineb
...
envfile -I /etc/default/bla
envfile /etc/default/bla-bla
my_cmd
Pyle answered 26/4, 2021 at 14:33 Comment(0)
V
0

My contribution to this is an expansion of the answer from @ user4040650 to allow for easy usage within a git repo. It will try to source the .env file from the current directory, or if that doesn't exist, the .env from the git repo you're in. It's helpful if you've cd'd into a child directory and then don't have to source ../../.env or whatnot.

I have this placed in my .bashrc, so I just need to call setenv where needed

setenv() {
  local env_path
  if { [ -f .env ] && env_path='.env'; } || { env_path=$(git  rev-parse --show-toplevel 2>/dev/null)/.env && [ -f "$env_path" ]; }; then
    echo "sourcing $env_path"
    set -o allexport
    source "$env_path"
    set +o allexport
  else
    echo "No env file found"
  fi
}
Verminous answered 21/7, 2021 at 6:18 Comment(0)
R
0
export $(grep -v '^#' envfilename | xargs -L 1  -d '\r' -d '\r\n')

This works like a charm on CentOS; When you have the problem of \r gets appended to the loaded variables. Also it take cares of comments and whitespaces.

Rumor answered 8/8, 2022 at 13:12 Comment(0)
E
0

A zsh way is to create a file on-the-fly that has export at the beginning of each line, source it in a subshell, and execute your command:

$ cat env.db
VAR=" value = with!! special chars #"
$ ( . =(sed 's/^[^#]/export \0/' < env.db) && echo $VAR) 
 value = with!! special chars #
$ echo $VAR

$
Erinn answered 2/10, 2022 at 19:48 Comment(0)
P
-1

For those who use ruby, I made a small utility gem called dotenv_export.

usage

dotenv_export is a small utility command which reads .env file and converts it into export statements using the ruby dotenv implementation.

# first install `dotenv_export`
gem install dotenv_export

Then, in your .bash_profile, or any shell environment in which you want to load the environment variables, execute following command:

eval "$(dotenv-export /path/to/.env)"
Photosensitive answered 14/9, 2020 at 2:39 Comment(1)
That is just silly - pulling 250MB+ of dependencies for something that can be done within every shell developed in the last 40+ years with two lines of code :-) ...but you could do worse, you could bring Docker into this ;-)Pyle

© 2022 - 2024 — McMap. All rights reserved.