Set environment variables from file of key/value pairs - bash

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.

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

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

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

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 $-.

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
It doesn't handle cases where the values have spaces in them. Commands such as env produce this format. – #Shardj

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
}

eval $(cat .env | sed 's/^/export /')

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.

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.

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.

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 $-.

Improving on Silas Paul's answer
exporting the variables on a subshell makes them local to the command.
(export $(cat .env | xargs) && rails c)

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}"
}

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.

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

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"'

Simpler:
grab the content of the file
remove any blank lines (just incase you separated some stuff)
remove any comments (just incase you added some...)
add export to all the lines
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

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)

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.

t=$(mktemp) && export -p > "$t" && set -a && . ./.env && set +a && . "$t" && rm "$t" && unset t
How it works
Create temp file.
Write all current environment variables values to the temp file.
Enable exporting of all declared variables in the sources script to the environment.
Read .env file. All variables will be exported into current environment.
Disable exporting of all declared variables in the sources script to the environment.
Read the contents of the temp file. Every line would have declare -x VAR="val" that would export each of the variables into environment.
Remove temp file.
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

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

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

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

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)

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

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

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.

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 :)

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

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.)

Related

Cleaner Way to load multiple environment variables into shell

I'm currently loading multiple variables into my shell (from a .env file) like so:
eval $(grep '^VAR_1' .env) && eval $(grep '^VAR_2' .env) && ...
I then use them in a script like so: echo $VAR_1.
Is there any way to condense this script into something like: eval $(grep ^('VAR_1|VAR_2')) .env? Maybe needs something other than grep
I would use source (.) command with process substitution:
. <(grep -e '^VAR_1=' -e '^VAR_2=' .env)
or
. <(grep '^VAR_[12]=' .env) # for this particular variables
The file .env must be from a trusted source, of course.
Note that the grep method won't work if a variable is assigned a string containing an embedded newline character.
You may use this grep with ERE option to filter all the variable you want from .env file:
grep -E '^(VAR_1|VAR_2)=' .env
This pattern will find VAR_1= or VAR_2= strings at the start.
To set variable use:
declare $(grep -E '^(VAR_1|VAR_2)=' .env)
# or
eval "$(grep -E '^(VAR_1|VAR_2)=' .env)"

How to read a variables stored in a file and use it in a bash code [duplicate]

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.
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
-o allexport enables all following variable definitions to be exported. +o allexport disables this feature.
set -o allexport
source conf-file
set +o allexport
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
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 $-.
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
It doesn't handle cases where the values have spaces in them. Commands such as env produce this format. – #Shardj
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
}
eval $(cat .env | sed 's/^/export /')
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.
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.
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.
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 $-.
Improving on Silas Paul's answer
exporting the variables on a subshell makes them local to the command.
(export $(cat .env | xargs) && rails c)
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}"
}
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.
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
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"'
Simpler:
grab the content of the file
remove any blank lines (just incase you separated some stuff)
remove any comments (just incase you added some...)
add export to all the lines
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
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)
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.
t=$(mktemp) && export -p > "$t" && set -a && . ./.env && set +a && . "$t" && rm "$t" && unset t
How it works
Create temp file.
Write all current environment variables values to the temp file.
Enable exporting of all declared variables in the sources script to the environment.
Read .env file. All variables will be exported into current environment.
Disable exporting of all declared variables in the sources script to the environment.
Read the contents of the temp file. Every line would have declare -x VAR="val" that would export each of the variables into environment.
Remove temp file.
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
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
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
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
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)
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
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
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.
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 :)
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
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.)

What export variables from file in Bash? [duplicate]

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.
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
-o allexport enables all following variable definitions to be exported. +o allexport disables this feature.
set -o allexport
source conf-file
set +o allexport
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
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 $-.
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
It doesn't handle cases where the values have spaces in them. Commands such as env produce this format. – #Shardj
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
}
eval $(cat .env | sed 's/^/export /')
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.
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.
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.
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 $-.
Improving on Silas Paul's answer
exporting the variables on a subshell makes them local to the command.
(export $(cat .env | xargs) && rails c)
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}"
}
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.
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
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"'
Simpler:
grab the content of the file
remove any blank lines (just incase you separated some stuff)
remove any comments (just incase you added some...)
add export to all the lines
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
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)
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.
t=$(mktemp) && export -p > "$t" && set -a && . ./.env && set +a && . "$t" && rm "$t" && unset t
How it works
Create temp file.
Write all current environment variables values to the temp file.
Enable exporting of all declared variables in the sources script to the environment.
Read .env file. All variables will be exported into current environment.
Disable exporting of all declared variables in the sources script to the environment.
Read the contents of the temp file. Every line would have declare -x VAR="val" that would export each of the variables into environment.
Remove temp file.
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
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
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
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
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)
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
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
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.
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 :)
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
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.)

Serialize a subset of environment variables

I'm trying to export some environment variables for use by a TomCat process.
There's a few ways to do this (I know how to solve the overall problem), but it bugged me that I didn't know how to do this particular shell task.
Tomcat recommends that all your environment customizations should be exported by "$CATALINA_HOME/bin/setenv.sh".
This whole thing is gonna be stuffed into a Docker container, so the only parameterizability will be via Docker env variables (let's assume for this task that I don't want to use volume mounts or create setenv.sh during the build process).
First, observe that docker run -e can be used to pass environment into the container:
🍔 docker run -eMY_VAR=SUP alpine env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=a528b6fc264b
MY_VAR=SUP
no_proxy=*.local, 169.254/16
HOME=/root
If we wanted to copy all of that env into setenv.sh, it's as simple as:
SETENV="/usr/local/tomcat/bin/setenv.sh"
echo '#!/bin/sh' > "$SETENV"
echo 'export -p' >> "$SETENV"
env >> "$SETENV"
But copying everything somewhat defeats the point of setenv.sh -- which is, to give your tomcat process a clean environment, with only intentional customizations.
So, we can agree on a convention for "which env vars are ones that we want to pass through to setenv.sh". Everything prefixed with MY_.
And now we get to an interesting shell problem.
env | grep '^MY_' | sed 's/^MY_/EXPORT /'
This gets us pretty close. Output looks like:
🍔 docker run -e MY_VAR=hey alpine sh -c "env | grep '^MY_' | sed 's/^MY_/EXPORT /'"
EXPORT VAR=hey
So, we've selected from the env command: only env vars prefixed with MY_. And we can redirect that output to setenv.sh.
Why do I say "pretty close"? Looks like we're done, right?
Try this for size:
🍔 docker run -e MY_VAR='multi
quote> line
quote> string' alpine sh -c "env | grep '^MY_' | sed 's/^MY_/EXPORT /'"
EXPORT VAR=multi
The script only worked for a simple subset of possibilities. i.e. we only managed to export the first line of our multi-line string.
For your convenience: env output for multi-line strings looks like this:
🍔 docker run -e MY_VAR='multi
line
string' alpine env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=0d0afaac6bec
MY_VAR=multi
line
string
no_proxy=*.local, 169.254/16
HOME=/root
I hesitate to try and tackle this using awk; there may be further string escaping complications that I have not considered.
I wonder whether there's a better way altogether to select & serialize a subset of exported environment?
EDIT: I negligently tagged this as a bash question, when really my intention was to pose an sh question. Specifically my intention is to get something that will work with no dependencies other than those that come with the alpine docker image. i.e. BusyBox sh, sed, grep, awk, env.
I've retained the bash tag so as not to punish the initial answer that was submitted when this was a bash-only question.
But I will give preference to an sh-compatible answer, and in particular to one that works with just the BusyBox UNIX utils.
So you need several things:
Enumerate the environment variables and select a subset.
For each selected environment variable, emit sh code that sets the variable to the desired value.
You can use export -p if you want to export all variables in a form that can be read back in, but parsing it to select only certain variables is harder. One way to make use of export -p is to unset the other variables. This only works if none of the environment variables is read-only, but you can work around that by running a separate shell instance (as opposed to a subshell).
To gather the list of variables to unset, you only need to get a superset of the list of all environment variables, and remove the ones you want to keep. You can easily do that by filtering the env output. I do that with a simple grep, you may want to use more complex code if your criteria for inclusion are more complex than “begins with a specific prefix”.
The occasional false positive due to a variable containing a newline followed by a valid variable name and an equal sign will only lead to calling unset on a non-existent variable, which does nothing. The desired variables are removed from the exclusion list, so the final output will never omit a desired variable.
excluded=$(env | LC_ALL=C sed -n 's/^\([A-Z_a-z][0-9A-Z_a-z]*\)=.*/\1/p' |
grep -v 'MY_')
sh -c 'unset $1; export -p' sh "$excluded" >setenv.sh
Dash prints an extra export PATH (with no value) if PATH was in the environment when it was invoked. If that bothers you, change sh -c … to (unset PATH; sh -c …).
Assuming GNU grep:
grep --null '^MY_' </proc/self/environ
...will emit your environment variables in NUL-delimited form (newlines intact).
Similarly, if you have bash:
while IFS= read -r -d '' vardef; do
[[ $vardef = MY_* ]] && printf '%s\0' "$vardef"
done </proc/self/environ
Note that if these variables were set in the same shell session, you may need to create a subprocess for /proc/self/environ to be updated:
(while IFS= read -r -d '' vardef; do
[[ $vardef = MY_* ]] && printf '%s\0' "$vardef"
done </proc/self/environ)
alpine image doesn't ship with bash.
You can use this script to extract all MY_* variables including newline variables:
docker run -e MY_FOO=bar -e MY_VAR="multi' export MY_INJECTED='val" -e MY_VAR2=$'multi
0MY_line=val
string' alpine sh -c "awk -v RS='\06' -F= '/^MY_/{k=\$1; sub(/^[^=]+=/, \"\");
gsub(/\047/, \"\047\\\\\\047\047\"); printf \"export %s=\047%s\047\n\", k, \$0
}' /proc/self/environ"
This will output:
export MY_FOO='bar'
export MY_VAR='multi'\'' export MY_INJECTED='\''val'
export MY_VAR2='multi
0MY_line=val
string'
Here is how awk works:
-v RS='\6': sets record separator as \6 works for nul byte as well (assuming you don't have \6 in value)
-F=: sets field separator as =
/^MY_/: Only process records starting with MY_
store variable name or $1 in variable k
Using sub function get part after = in $0
Using print format output so that it can be used in $CATALINA_HOME/bin/setenv.sh file.
\047 is for printing single quote
what about
declare -p ${!MY_*}
and
declare -p ${!MY_*} | sed -r 's/^declare (-[^ ]*)* MY_/export /'
or
declare -p ${!MY_*} | sed 's/^declare \(-[^ ]*\)* MY_/export /'
EDIT posix compliant version :
some env or printenv accept -0 option to end each output line by \0 rather than a newline. Thus
env -0 | perl -ne 'BEGIN{$/="\0";$\="\n";$q="\047"}next unless /^MY_/;chomp;s/$q/$q\\$q$q/;s/=/=$q/;s/$/$q/;print'
How it works
$/ : input record separator
$\ : output record separator
$q : variable to store single quote (\047) because of surrounding single quotes in command
next : to filter "MY_" variables
chomp : removes the input separator
s/// : quote substitution
EDIT: variation of perl version in posix shell
env -0 | xargs -0 sh -c 'for entry; do [[ $entry = MY_* ]] || continue; printf "%s=\047%s\047\n" "${entry%%=*}" "$(echo "${entry#*=}" | sed '\''s/\x27/\x27\\\x27\x27/g'\'' )"; done' -

Need bash shell script for reading name value pairs from a file

I have a file like
name1=value1
name2=value2
I need to read this file using shell script and set variables
$name1=value1
$name2=value2
Please provide a script that can do this.
I tried the first answer below, i.e. sourcing the properties file but I'm getting a problem if the value contains spaces. It gets interpreted as a new command after the space. How can I get it to work in the presence of spaces?
If all lines in the input file are of this format, then simply sourcing it will set the variables:
source nameOfFileWithKeyValuePairs
or
. nameOfFileWithKeyValuePairs
Use:
while read -r line; do declare "$line"; done <file
Sourcing the file using . or source has the problem that you can also put commands in there that are executed. If the input is not absolutely trusted, that's a problem (hello rm -rf /).
You can use read to read key value pairs like this if there's only a limited known amount of keys:
read_properties()
{
file="$1"
while IFS="=" read -r key value; do
case "$key" in
"name1") name1="$value" ;;
"name2") name2="$value" ;;
esac
done < "$file"
}
if your file location is /location/to/file and the key is mykey:
grep mykey $"/location/to/file" | awk -F= '{print $2}'
Improved version of #robinst
read_properties()
{
file="$1"
while IFS="=" read -r key value; do
case "$key" in
'#'*) ;;
*)
eval "$key=\"$value\""
esac
done < "$file"
}
Changes:
Dynamic key mapping instead of static
Supports (skips) comment lines
A nice one is also the solution of #kurumi, but it isn't supported in busybox
And here a completely different variant:
eval "`sed -r -e "s/'/'\\"'\\"'/g" -e "s/^(.+)=(.+)\$/\1='\2'/" $filename`"
(i tried to do best with escaping, but I'm not sure if that's enough)
suppose the name of your file is some.properties
#!/bin/sh
# Sample shell script to read and act on properties
# source the properties:
. some.properties
# Then reference then:
echo "name1 is $name1 and name2 is $name2"
Use shdotenv
dotenv support for shell and POSIX-compliant .env syntax specification
https://github.com/ko1nksm/shdotenv
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
Load the .env file into your shell script.
eval "$(shdotenv [OPTION]...)"
sed 's/^/\$/' yourfilename

Resources