Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Parse a .env (dotenv) file directly using BASH
# Pass the env-vars to MYCOMMAND
eval $(egrep -v '^#' .env | xargs) MYCOMMAND
# … or ...
# Export the vars in .env into your shell:
export $(egrep -v '^#' .env | xargs)
@kowalk

This comment has been minimized.

Copy link

kowalk commented Feb 16, 2018

If you want just set a single variable in your script just use code below:
MY_VAR=$(grep MY_VAR .env | xargs)
IFS='=' read -ra MY_VAR <<< "$MY_VAR"
MY_VAR=${MY_VAR[1]}

@rafaelbeckel

This comment has been minimized.

Copy link

rafaelbeckel commented Mar 17, 2018

read_var() {
    VAR=$(grep $1 $2 | xargs)
    IFS="=" read -ra VAR <<< "$VAR"
    echo ${VAR[1]}
}

MY_VAR=$(read_var MY_VAR .env)
@desprit

This comment has been minimized.

Copy link

desprit commented Mar 26, 2018

MY_VAR=$(grep MY_VAR .env | xargs)
MY_VAR=${MY_VAR#*=}

dunno about possible drawbacks though

@pronebird

This comment has been minimized.

Copy link

pronebird commented May 27, 2018

It won't work if you have spaces, i.e FOO="BAR BUZ" will be returned without quotes from xargs

@revolter

This comment has been minimized.

Copy link

revolter commented May 30, 2018

Use xargs -d '\n' on GNU systems or xargs -0 on BSD systems to handle values with spaces.

https://stackoverflow.com/a/32589977/865175

@amberdiehl

This comment has been minimized.

Copy link

amberdiehl commented Aug 10, 2018

I have nothing technical to add; just saying thank you. This is exactly what I needed.

@dovidweisz

This comment has been minimized.

Copy link

dovidweisz commented Aug 30, 2018

Here's a one-liner for getting one variable:

MY_VAR=$(grep MY_VAR .env | cut -d '=' -f2)

One issue i see with all these solutions is: what happens if i have two similar variables. I need to fix the Grep part to only match ^MY_VAR=. I'm just not that good with grep.

Also... i would never have thought about using grep here, so Thank You.

@damcclean

This comment has been minimized.

Copy link

damcclean commented Nov 10, 2018

Perfect! Exactly what I was looking for :)

@twalling

This comment has been minimized.

Copy link

twalling commented Dec 14, 2018

I had to change the one-liner by @dovidweisz so that all of my var was included. If you have equal signs in your var which we're also using as the delimiter then you have to tell cut to include all the rest of the fields

MY_VAR=$(grep MY_VAR .env | cut -d '=' -f 2-)
@msmans

This comment has been minimized.

Copy link

msmans commented Dec 20, 2018

With GNU grep, you can simply do MY_VAR=$(grep -oP '^MY_VAR=\K.*' .env)

@Jules-Baratoux

This comment has been minimized.

Copy link

Jules-Baratoux commented Dec 26, 2018

I use the following to avoid overriding already set variable:

source <(grep -v '^#' .env | sed -E 's|^(.+)=(.*)$|: ${\1=\2}; export \1|g')
@heycarsten

This comment has been minimized.

Copy link

heycarsten commented Jan 2, 2019

I’ve been doing it like this:

# Load up .env
set -o allexport
[[ -f .env ]] && source .env
set +o allexport

It seems to work great… are their drawbacks to this?

In my case I’m just loading in the variables to use for in-project commands (in bin)

✌️

@dazza-codes

This comment has been minimized.

Copy link

dazza-codes commented Feb 10, 2019

When this is in a script, allow an existing ENV variable or CLI to override the .env file and only try to use a .env file if it exists:

test -z "$VAR" && test -f .env && \
    VAR=$(grep -e '^VAR=.*' .env | cut -d '=' -f2)

Add an export VAR if necessary.

@seeekr

This comment has been minimized.

Copy link

seeekr commented Mar 5, 2019

If you're having trouble with spaces still messing up your loading of the variables, use this:

IFS='
'
export $(egrep -v '^#' .env | xargs -0)
IFS=

(Replace xargs -0 with xargs -d '\n' if you're not on macOS.)

Explanation: IFS changes what bash splits strings on. By default it splits on spaces, but using the above it only does so on newlines. We reset its behavior after we're done.

@TechupBusiness

This comment has been minimized.

Copy link

TechupBusiness commented Apr 7, 2019

This works fine (as long as you don't use "=" in your values):

read_var() {
    VAR=$(grep "^$1=" $2 | xargs)
    IFS="=" read -ra VAR <<< "$VAR"
    IFS=" "
    echo ${VAR[1]}
}
@miguelmota

This comment has been minimized.

Copy link

miguelmota commented Jun 24, 2019

Here's a version to make the filename optional and use .env as default:

read_var() {
  if [ -z "$1" ]; then
    echo "environment variable name is required"
    return
  fi

  local ENV_FILE='.env'
  if [ ! -z "$2" ]; then
    ENV_FILE="$2"
  fi

  local VAR=$(grep $1 "$ENV_FILE" | xargs)
  IFS="=" read -ra VAR <<< "$VAR"
  echo ${VAR[1]}
}

Example usage:

local my_var=$(read_var MY_VAR)
local my_other_var=$(read_var MY_VAR staging.env)
@AdamGerthel

This comment has been minimized.

Copy link

AdamGerthel commented Jul 10, 2019

God I wish it was possible to upvote the different solutions here. I have no idea which one to pick.

@judy2k

This comment has been minimized.

Copy link
Owner Author

judy2k commented Jul 10, 2019

@AdamGerthel bear in mind that there are actually two different problems being solved in these solutions. The original is parsing a whole .env file - many of the other 'solutions' are extracting a single value from a .env file.

(This was never intended to be a popular/controversial gist - I was just putting it here for my own use!)

@AdamGerthel

This comment has been minimized.

Copy link

AdamGerthel commented Jul 10, 2019

@judy2k right - and I eventually found this on SO: https://stackoverflow.com/a/20909045/930998

@cdcabrera

This comment has been minimized.

Copy link

cdcabrera commented Jul 12, 2019

Awesome @miguelmota much appreciated! I updated it a little

read_var() {
  local ENV_FILE="${2:-./.env}"
  local VAR=$(grep $1 "$ENV_FILE" | xargs)

  IFS="=" read -ra VAR <<< "$VAR"
  echo ${VAR[1]}
}
@Norcoen

This comment has been minimized.

Copy link

Norcoen commented Aug 8, 2019

I’ve been doing it like this:

# Load up .env
set -o allexport
[[ -f .env ]] && source .env
set +o allexport

It seems to work great… are their drawbacks to this?

In my case I’m just loading in the variables to use for in-project commands (in bin)

v

Personally I use

set -a
[ -f .env ] && . .env
set +a

it does the same, but it should be POSIX compatible

@peteratticusberg

This comment has been minimized.

Copy link

peteratticusberg commented Oct 26, 2019

export $(cat .env) works for basic use cases

@cocowalla

This comment has been minimized.

Copy link

cocowalla commented Nov 15, 2019

export $(cat .env) works for basic use cases

What kind of cases does it not work for

EDIT: Answering my own question: it doesn't work for quoted strings

@dobesv

This comment has been minimized.

Copy link

dobesv commented Nov 19, 2019

@AlexSkrypnyk

This comment has been minimized.

Copy link

AlexSkrypnyk commented Jan 20, 2020

What is important is to make sure that environment variables' values takes precedence over values in .env file.

Using this script solves this:

t=$(mktemp) && export -p > "$t" && set -a && . ./.env && set +a && . "$t" && rm "$t" && unset t
@ktomk

This comment has been minimized.

Copy link

ktomk commented Feb 9, 2020

using a dot env file format like in the docker project (supports comments, setting and imports [1]):

<.env.dist | sed -n 's/^[^#]/export /p' | source /proc/self/fd/0

Your mileage may vary. source requires a file-name.

[1]: Docker Run: Set environment variables (-e, --env, --env-file) https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file

@rsoury

This comment has been minimized.

Copy link

rsoury commented Feb 13, 2020

read_var() {
    VAR=$(grep $1 $2 | xargs)
    IFS="=" read -ra VAR <<< "$VAR"
    echo ${VAR[1]}
}

MY_VAR=$(read_var MY_VAR .env)

Thanks, this answer worked well, however, for exact matching with grep:

read_var() {
     VAR=$(grep -w $1 $2 | xargs)
     IFS="=" read -ra VAR <<< "$VAR"
     echo ${VAR[1]}
}
 
MY_VAR=$(read_var MY_VAR .env)
@abdennour

This comment has been minimized.

Copy link

abdennour commented Feb 27, 2020

This single line had worked with me since 2 years and I am using it everywhere now , even MacOS:

export $(cat .env | xargs)

Do not forget "xargs".

@judy2k

This comment has been minimized.

Copy link
Owner Author

judy2k commented Mar 3, 2020

export $(cat .env | xargs)

@abdennour This is the same as the original solution, but the original also filters out comment lines beginning with #

@sarink

This comment has been minimized.

Copy link

sarink commented Mar 16, 2020

@Blackjacx

This comment has been minimized.

Copy link

Blackjacx commented Mar 23, 2020

Why not just using source .env

@loopmode

This comment has been minimized.

Copy link

loopmode commented Mar 24, 2020

I had trouble using the initial method for values that were URLs, very simple ones actually (e.g HOST=http://localhost:3000)

I then went with source .env and all is good now.

@bmmuller

This comment has been minimized.

Copy link

bmmuller commented Apr 3, 2020

The function below takes the env file as a parameter and handles spaces, comments and single/double quotes properly.

function export_envs() {
  local envFile=${1:-.env}
  while IFS='=' read -r key temp || [ -n "$key" ]; do
    local isComment='^[[:space:]]*#'
    local isBlank='^[[:space:]]*$'
    [[ $key =~ $isComment ]] && continue
    [[ $key =~ $isBlank ]] && continue
    value=$(eval echo "$temp")
    eval export "$key='$value'";
  done < $envFile
}
@ktomk

This comment has been minimized.

Copy link

ktomk commented Apr 4, 2020

@bmmuller: does it handle imports? e.g. only export a variable if it exists yet (not null-ify is)? and what is intended the portability of your code example (/bin/bash or /bin/sh)?

@mgrachev

This comment has been minimized.

Copy link

mgrachev commented Jul 7, 2020

In addition to using environment variables I can recommend the tool https://github.com/dotenv-linter/dotenv-linter  — it’s a lightning-fast linter for .env files. Written in Rust. Maybe it would be useful for you.

@m-b-davis

This comment has been minimized.

Copy link

m-b-davis commented Jul 8, 2020

Here's a version to make the filename optional and use .env as default:

read_var() {
  if [ -z "$1" ]; then
    echo "environment variable name is required"
    return
  fi

  local ENV_FILE='.env'
  if [ ! -z "$2" ]; then
    ENV_FILE="$2"
  fi

  local VAR=$(grep $1 "$ENV_FILE" | xargs)
  IFS="=" read -ra VAR <<< "$VAR"
  echo ${VAR[1]}
}

Example usage:

local my_var=$(read_var MY_VAR)
local my_other_var=$(read_var MY_VAR staging.env)

I had two problems with this

First - This was failing for me when having a var which is a substring of another var e.g

API_PORT=5000
PORT=4000

I would get PORT as 5000 PORT instead of 4000.

Second: if an env var was named incorrectly e.g PORTX=4000 it would still load PORT

To fix, I used a carat before the name to match on the start of the line and an equals after (see grep $1)

read_var() {
  if [ -z "$1" ]; then
    echo "environment variable name is required"
    return
  fi

  local ENV_FILE='.env'
  if [ ! -z "$2" ]; then
    ENV_FILE="$2"
  fi

  local VAR=$(grep ^$1= "$ENV_FILE" | xargs)
  IFS="=" read -ra VAR <<< "$VAR"
  echo ${VAR[1]}
}
@GaMoCh

This comment has been minimized.

Copy link

GaMoCh commented Jul 16, 2020

Remove spaces and lines that start with '#'

export $(sed 's/[[:blank:]]//g; /^#/d' .env | xargs)
@sandhawke

This comment has been minimized.

Copy link

sandhawke commented Jul 24, 2020

Why not just using source .env

Because the syntax of .env files (X=y) is used in the Bourne shell and its derivatives to set local shell variables, not environment variables. Shell variables just effect the behavior of that shell process, where environment variables affect programs run from that shell, which is what you want with .env.

To set an environment variable you need to use the 'export' command (like "export X=y") or the allexport (-a) setting seen in the solutions above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.