从密钥/对值文件中设置环境变量

时间:2013-10-12 07:01:30

标签: bash variables environment-variables

TL; DR:如何将文本文件中的一组键/值对导出到shell环境中?


对于记录,下面是问题的原始版本,带有示例。

我正在用bash编写一个脚本,用于解析某个文件夹中包含3个变量的文件,这是其中之一:

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

此文件存储在./conf/prac1

我的脚本minientrega.sh然后使用以下代码解析文件:

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

但是当我在命令行中执行minientrega.sh prac1时,它不会设置环境变量

我也尝试使用source ./conf/$1,但同样的问题仍然适用

也许有其他方法可以做到这一点,我只需要使用我传递的文件的环境变量作为我的脚本的参数。

37 个答案:

答案 0 :(得分:625)

这可能会有所帮助:

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

我使用它的原因是我想在rails控制台中测试.env内容。

gabrielf想出了一个将变量保持在本地的好方法。这解决了从项目到项目的潜在问题。

env $(cat .env | xargs) rails

我已使用bash 3.2.51(1)-release

对此进行了测试

<强>更新

要忽略以#开头的行,请使用此功能(感谢Pete's comment):

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

如果你想unset文件中定义的所有变量,请使用:

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

<强>更新

要使用空格处理值,请使用:

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

在GNU系统上或:

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

在BSD系统上。

答案 1 :(得分:283)

-o allexport可以导出所有后续变量定义。 +o allexport禁用此功能。

set -o allexport
source conf-file
set +o allexport

答案 2 :(得分:141)

您的方法问题是export循环中的while发生在子shell中,并且这些变量在当前shell(while循环的父shell)中不可用。

在文件中添加export命令:

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

然后你需要使用以下命令在当前shell中的文件中输入:

. ./conf/prac1

OR

source ./conf/prac1

答案 3 :(得分:72)

set -a
. ./env.txt
set +a

如果env.txt如下:

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

答案 4 :(得分:38)

我发现最有效的方法是:

export $(xargs < .env)

说明

当我们有一个.env文件时,

key=val
foo=bar

运行xargs < .env将获得key=val foo=bar

所以我们将得到一个export key=val foo=bar,这正是我们所需要的!

限制

  1. 它不处理值中包含空格的情况。诸如env之类的命令会产生这种格式。 – @Shardj

答案 5 :(得分:25)

此处的其他几个答案中提到了allexport选项,其中set -a是快捷方式。获取.env实际上比循环和导出更好,因为它允许注释,空行,甚至是命令生成的环境变量。我的.bashrc包含以下内容:

# .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
}

答案 6 :(得分:21)

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

答案 7 :(得分:20)

这是另一个sed解决方案,它不运行eval或需要ruby:

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

这会添加导出,在注释开头的行上保留注释。

.env内容

A=1
#B=2

样本运行

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

我发现这在构建这样一个文件以便在systemd unit file, with EnvironmentFile中加载时特别有用。

答案 8 :(得分:13)

我赞成了user4040650的答案,因为它既简单又容许文件中的注释(即以#开头的行),这对我来说是非常需要的,因为可以添加解释变量的注释。只需在原始问题的背景下重写。

如果脚本按照指示调用:minientrega.sh prac1,则minientrega.sh可以:

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

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

以下内容摘自set documentation

  

这个内置非常复杂,值得拥有自己的部分。组   允许您更改shell选项的值并设置   位置参数,或显示shell的名称和值   变量

     

设置[--abefhkmnptuvxBCEHPT] [-o选项名称] [参数...]设置   [+ abefhkmnptuvxBCEHPT] [+ o option-name] [参数...]

     

如果未提供选项或参数,则set将显示所有shell的名称和值   变量和函数,根据当前语言环境排序,在   格式可以重复用作设置或重置的输入   当前设定的变量。只读变量无法重置。在POSIX中   模式,只列出了shell变量。

     

提供选项时,它们会设置或取消设置shell属性。   选项(如果指定)具有以下含义:

     

-a创建或修改的每个变量或函数都被赋予export属性并标记为导出到环境中   后续命令。

这也是:

  

使用“+”而不是“ - ”会导致关闭这些选项。该   在调用shell时也可以使用options。目前的设定   选项可以在$ - 。

中找到

答案 9 :(得分:11)

不完全确定为什么或我错过了什么,但是在大多数答案都失败之后却失败了。我意识到有了这个.env文件:

MY_VAR="hello there!"
MY_OTHER_VAR=123

我可以简单地做到这一点:

source .env
echo $MY_VAR

输出:Hello there!

似乎可以在Ubuntu linux上正常工作。

答案 10 :(得分:11)

改善Silas Paul的回答

在子shell上导出变量使它们成为命令的本地变量。

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

答案 11 :(得分:11)

这是我的变体:

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

与先前的解决方案相比:

  • 它不会泄漏超出范围的变量(.env中的值不会暴露给调用者)
  • 不破坏set选项
  • 返回已执行命令的退出代码
  • 使用与posix兼容的set -a
  • 使用.而非source来避免暴力行为
  • 如果.env加载失败,则不会调用
  • 命令
with_env rails console

答案 12 :(得分:9)

简单:

  1. 抓取文件内容
  2. 删除所有空行(只是让你分开一些东西)
  3. 删除所有评论(只是因为你添加了一些评论......)
  4. export添加到所有行
  5. eval整件事
  6. eval $(cat .env | sed -e /^$/d -e /^#/d -e 's/^/export /')

    另一种选择(你不必运行eval(感谢@Jaydeep)):

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

    最后,如果您想让自己的生活变得轻松,请将其添加到~/.bash_profile

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

    (请确保你重新安装你的BASH设置!!! source ~/.bash_profile或..只需创建一个新的标签/窗口并解决问题)你这样称呼它:source_envfile .env

答案 13 :(得分:8)

source 的问题在于它要求文件具有正确的 bash 语法,而一些特殊字符会破坏它:="'<> 等。所以在某些情况下你可以

source development.env

它会起作用。

然而,这个版本可以承受值中的每个特殊字符

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

说明:

  • -a 表示每个 bash 变量都将成为环境变量
  • /^#/d 删除评论(以 # 开头的字符串)
  • /^\s*$/d 删除空字符串,包括空格
  • "s/'/'\\\''/g"'\'' 替换每个单引号,这是 bash 中生成引号的技巧序列:)
  • "s/=\(.*\)/='\1'/g" 将每个 a=b 转换为 a='b'

因此,您可以使用特殊字符:)

要调试此代码,请将 source 替换为 cat,您将看到此命令产生的结果。

答案 14 :(得分:5)

在其他答案的基础上,以下是一种仅导出文件中一行子集的方法,包括PREFIX_ONE="a word"之类的空格值:

set -a
. <(grep '^[ ]*PREFIX_' conf-file)
set +a

答案 15 :(得分:5)

您可以使用原始脚本设置变量,但需要按以下方式调用它(使用独立点):

. ./minientrega.sh

cat | while read方法也可能存在问题。我建议使用方法while read line; do .... done < $FILE

这是一个有效的例子:

> 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

答案 16 :(得分:5)

如果 env 支持 -S 选项,则可以使用换行符或转义字符,如 \n\t(参见 env):

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

.env 文件示例:

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

测试:

env -S "$(cat .env)" sh -c 'echo "$KEY"'

答案 17 :(得分:3)

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

工作方式

  1. 创建临时文件。
  2. 将所有当前环境变量值写入临时文件。
  3. 启用将源脚本中所有声明的变量导出到环境中。
  4. 读取.env文件。所有变量都将导出到当前环境中。
  5. 禁止将源脚本中所有声明的变量导出到环境中。
  6. 读取临时文件的内容。每行将有declare -x VAR="val",可将每个变量导出到环境中。
  7. 删除临时文件。
  8. 取消保存临时文件名的变量。

功能

  • 保留环境中已设置的变量的值
  • .env可以发表评论
  • .env可以有空行
  • .env不需要像其他答案(set -aset +a)中那样的特殊页眉或页脚
  • .env不需要每个值都有export
  • 单线

答案 18 :(得分:2)

我使用这个:

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)

首先删除评论:

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

然后转换为空定界符而不是换行符:

tr '\n' '\000'

然后将null替换为null:

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

然后将打印对打印到有效的带引号的bash导出中(对%q使用bash printf):

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

然后将所有资源全部采购。

它几乎适用于所有带有特殊字符的情况。

答案 19 :(得分:2)

由@Dan Kowalczyk修改

我将其放在~/.bashrc中。

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

与Oh-my-Zsh的dotenv插件具有很好的交叉兼容性。 (有Oh-my-bash,但没有dotenv插件。)

答案 20 :(得分:2)

使用 shdotenv

dotenv 支持 shell 和符合 POSIX 的 .env 语法规范
https://github.com/ko1nksm/shdotenv

eval "$(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

要求

shdotenv 是一个带有嵌入式 awk 脚本的单文件 shell 脚本。

  • POSIX shell(破折号、bash、ksh、zsh 等)
  • awk(gawk、nawk、mawk、busybox awk)

答案 21 :(得分:1)

中的空格

这里有很多很棒的答案,但我发现它们都缺乏对空白区域的支持:

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

我找到了两个解决方案,这些解决方案可以支持空行和注释。

一个基于sed和@ javier-buzzi answer

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

一个基于@ john1024 answer

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

这里的关键是使用declare -x并将行放在双引号中。我不知道为什么,但是当你将循环代码重新格式化为多行时它将不起作用 - 我不是bash程序员,我只是吞噬了这些,它对我来说仍然是神奇的:)

答案 22 :(得分:1)

我的要求是:

  • 没有前缀export的简单.env文件(与dotenv兼容)
  • 用引号引起来的值:TEXT =“ alpha bravo charlie”
  • 支持带有#号和空行的注释
  • 适用于mac / BSD和linux / GNU的通用

根据以上答案编译的完整版本:

  set -o allexport
  eval $(grep -v '^#' .env | sed 's/^/export /')
  set +o allexport

答案 23 :(得分:1)

我对之前建议的解决方案有疑问:

  • @ anubhava的解决方案使得编写bash友好配置文件的速度非常快,而且 - 您可能不想总是导出配置。
  • @Silas Paul解决方案在您的变量有空格或其他字符在引用值中运行良好时会中断,但$()会弄得一团糟。

这是我的解决方案,这仍然是非常可怕的IMO - 并且没有解决'#34;仅导出给一个孩子&#34; Silas解决的问题(虽然您可以在子shell中运行它以限制范围):

source .conf-file
export $(cut -d= -f1 < .conf-file)

答案 24 :(得分:0)

我如何保存变量:

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

我如何加载它们

source myvariables.sh

答案 25 :(得分:0)

如果您打算将 exec 作为脚本的最后一个命令,您可以通过使用 execlineb 解释器获得一个附加选项。这是您脚本的最后一行的样子:

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

envfile ... 是来自 execline 套件的命令,它们依赖于“chain loading”。 顺便说一句,一旦你进入这个兔子洞,你可能会发现你不再需要 shell(......并重新考虑你的其他生活选择:-) 通过使用 execlineb 解释器以最小的开销启动服务非常有用而不是完全的外壳,即:

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

答案 26 :(得分:0)

我对此的贡献是对@的answer的扩展 user4040650 允许在 git repo 中轻松使用。它将尝试从当前目录中获取 .env 文件,或者如果它不存在,则从您所在的 git repo 中获取 .env 。如果您已经 cd 进入子目录然后不这样做会很有帮助不必提供 ../../.env 或诸如此类的东西。

我把它放在我的 .bashrc 中,所以我只需要在需要的地方调用 setenv

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
}

答案 27 :(得分:0)

这是我的看法。我有以下要求:

  • 忽略评论行
  • 在值中允许空格
  • 允许空行
  • 能够在默认为.env的情况下传递自定义的环境文件
  • 允许导出以及内联运行命令
  • 如果环境文件不存在则退出
source_env() {
  [ "$#" -eq 1 ] && env="$1" || env=".env"
  [ -f "$env" ] || { echo "Env file $env doesn't exist"; return 1; }
  eval $(grep -Ev '^#|^$' "$env" | sed -e 's/=\(.*\)/="\1/g' -e 's/$/"/g' -e 's/^/export /')
}

将函数保存到您的.bash_profile或等效文件后的用法:

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

受到Javier和其他一些评论的启发。

答案 28 :(得分:0)

此代码处理RHS上的空格,并跳过诸如bash模块定义之类的“怪异”变量(其中带有“()”):

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

答案 29 :(得分:0)

我的.env文件如下:

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

使用@henke的方式,导出的值最终包含引号"

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

但是我希望导出的值仅包含:

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

要解决此问题,我编辑命令以删除引号:

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

答案 30 :(得分:0)

尝试这样的事情

for line in `cat your_env_file`; do if [[ $line != \#* ]];then export $line; fi;done

答案 31 :(得分:0)

我找到的最短方法:

您的.env文件:

VARIABLE_NAME="A_VALUE"

然后就

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

奖金:因为它很短,所以在package.json文件中非常有用

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

答案 32 :(得分:0)

我在Mac上使用docker-compose和.env文件,并想将.env导入我的bash shell(以进行测试),这里的“最佳”答案是以下变量:

.env

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

解决方案

所以我最终使用了eval,并将env var defs括在单引号中。

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

流行版本

$ /bin/bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.

答案 33 :(得分:0)

首先,创建一个环境文件,该文件将具有如下所示环境的所有键值对,并根据您的喜好将其命名为env_var.env

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

然后创建一个脚本,该脚本将为python环境导出所有环境变量,如下所示,并将其命名为export_env.sh

#!/usr/bin/env bash

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

set -o allexport
source $ENV_FILE
set +o allexport

$CMD

此脚本将第一个参数作为环境文件,然后导出该文件中的所有环境变量,然后在其后运行命令。

用法:

./export_env.sh env_var.env python app.py

答案 34 :(得分:0)

当我尝试在shell中重用Docker --env-file时,我遇到了这个帖子。 Their format不兼容bash,但很简单:name=value,没有引用,没有替换。他们还会忽略空白行和#条评论。

我无法让它与posix兼容,但是这里应该可以在类似bash的shell中工作(在OSX 10.12.5上测试zsh,在Ubuntu 14.04上测试bash):

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

不会处理上述文档中示例中的三个案例:

  • bash: export: `123qwe=bar': not a valid identifier
  • bash: export: `org.spring.config=something': not a valid identifier
  • 并且它不会处理直通语法(裸FOO

答案 35 :(得分:0)

我的.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

调用:

source .env; echo $USER; echo $PASS

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

答案 36 :(得分:-1)

对于那些使用红宝石的人,我制作了一个名为dotenv_export的实用工具。

用法

dotenv_export是一个小的实用程序命令,它读取.env文件,并使用the ruby dotenv implementation将其转换为export语句。

# first install `dotenv_export`
gem install dotenv_export

然后,在您的.bash_profile或要在其中加载环境变量的任何Shell环境中,执行以下命令:

eval "$(dotenv-export /path/to/.env)"