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


511

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但同样的问题仍然适用

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




这是一个很大的问题,但是用特定的变量名(“ MINIENTREGA_FECHALIMITE”?这是什么意思?)和数字(3)来措辞太具体。普遍的问题很简单,即“如何将一组键/值对从文本文件导出到Shell环境中”。
Dan Dascalescu

而且,这已经在unix.SE上得到了解答,并且可以说在那里更具话题性。
Dan Dascalescu

Answers:


208

你的方法的问题是exportwhile循环中一个子壳发生,这些变量不会在当前的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

要么

source ./conf/prac1

4
尽管逐行读取文件并将每行传递到export不理想,但也可以通过在循环上简单地使用输入重定向来解决此问题while read line; do ... ; done < ./conf/$1
chepner 2014年

4
如果不是来自文件,请使用< <(commands that generate output)
o11c

5
您有一个更干净的解决方案,我更喜欢set -o allexport
heralight

2
如果在系统之间使用此.env文件,export则在Java,SystemD或其他工具插入时会破坏该文件
FilBot3

1
awk '{print "export " $0}' envfile方便性命令,可在每行开始之前添加导出
Shardj

887

这可能会有所帮助:

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

我之所以使用它,是因为我想.env在Rails控制台中进行测试。

gabrielf提出了一种使变量保持局部的好方法。这解决了从一个项目到另一个项目时的潜在问题。

env $(cat .env | xargs) rails

我已经用 bash 3.2.51(1)-release


更新:

要忽略以开头的行#,请使用以下命令(感谢Pete的注释):

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系统上。


6
谢谢,我喜欢这不需要在文件前添加任何内容-允许与Foreman(Procfile).env格式兼容。
natevw 2014年

29
我想出了解决方案:env $(cat .env | xargs)rails
gabrielf 2014年

4
如果任何env值都带有空格,这似乎不起作用,尽管我实际上不确定用空格指定值的最佳/期望方式是什么。github.com/ddollar/foreman/issues/56说它应该像这样工作,export $(cat .env)但是我不知道如何处理空格。
Dan Benamy 2015年

6
@BenjaminWheeler GNU linux具有-d设置分隔符的功能,因此我正在尝试使用env $(cat .env | xargs -d '\n') rails,但是如果.env有空格,则找不到文件仍会出错。知道为什么这行不通吗?
Bailey Parker

19
这是一个较短的变化eval $(cat .env) rails
manalang

412

-o allexport允许导出以下所有变量定义。+o allexport禁用此功能。

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

9
奇迹般有效!即使.env文件中有注释。谢谢!
Slava Fomin II

9
并排成一行set -o allexport; source conf-file; set +o allexport
HarlemSquirrel 2016年

1
当Jenkins EnvInject插件不起作用时,这是一种读取属性文件的好方法。谢谢!
Teresa Peters

21
@CMCDragonkai,对于POSIX,应该是set -a; . conf-file; set +a
查尔斯·达菲

3
如果环境变量中有空格,则此方法有效。其他许多人没有。尽管使用eval()方法确实有效,但是使用eval也会使我感到有些奇怪
CommandZ,2018年

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

如果env.txt是这样的:

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

说明 -a等效于allexport。换句话说,shell中的每个变量分配都被导出到环境中(供多个子进程使用)。可以在Set内置文档中找到更多信息:

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

使用“ +”而不是“-”会导致这些选项被关闭。这些选项也可以在调用Shell时使用。当前的选项集可以在$-中找到。


您能解释一下-a和+ a吗?
奥托(Otto)

11
@Otto -a等效于allexport。换句话说,shell中的每个变量分配都被export编入环境中(以供多个子进程使用)。另请参阅本文gnu.org/software/bash/manual/html_node/The-Set-Builtin.html
Dan Kowalczyk

33

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
}

3
这看起来不错,但是您离开目录时是否卸载环境变量?
巴斯蒂安·文图尔

1
我没有设置变量,这从来都不是问题。我的应用程序倾向于使用不同的变量名,如果存在重叠,我将在.env中将其设置为空白VAR=
gsf

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

1
使用eval $(cat .env | sed 's/^[^$]/export /')可以使空行,以提高可读性。
Mario Uher 2015年

2
我发现这cat .env | sed 's/^[^$]/export /'去除了初始字符。即A=foo\nB=bar\n我得到的文件export =foo\nexport =bar\n。跳过空白行效果更好cat .env | sed '/^$/! s/^/export /'
Owen S.

(我也注意到,对于UNIX代码高尔夫球手来说,cat在任何一种情况下都不需要:也eval $(sed 's/^/export /' .env)可以工作。)
Owen S.

21

我发现最有效的方法是:

export $(xargs < .env)

说明

当我们有这样的.env文件时:

key=val
foo=bar

xargs < .env会得到key=val foo=bar

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

局限性

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

3
它不能处理值中包含空格的情况。诸如此类的命令会env产生这种格式。
Shardj

19

这是另一个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单位文件中的文件时,EnvironmentFile我发现这特别有用。


不支持OSX中的多行
Abdennour TOUMI 17/12/3

17

我赞成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文档中摘录的:

这个内建函数是如此复杂,以至于它自己的部分。set允许您更改shell选项的值并设置位置参数,或显示shell变量的名称和值。

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

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

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

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

还有:

使用“ +”而不是“-”会导致这些选项被关闭。这些选项也可以在调用Shell时使用。当前的选项集可以在$-中找到。


14

改善Silas Paul的答案

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

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


然后,您可以使用它(set -a; source dev.env; set +a; rails c)来获得采购的好处(例如可以执行脚本)。
wacha

12

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

这将保存/恢复您的原始选项,无论它们是什么。

使用set -o allexport具有在不使用正则表达式的情况下正确跳过注释的优点。

set +o本身以bash以后可以执行的格式输出您当前的所有选项。还方便:set -o仅以人类友好的格式输出当前的所有选项。


2
如果您需要取消仅在中设置的变量,我可能会exec env -i bash在调用之前清除现有环境。eval.env
b4hand 2015年

11

我找到的最短方法:

您的.env档案:

VARIABLE_NAME="A_VALUE"

然后就

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

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

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

如果您有很多变量怎么办?
马多

@Madeo,您可以根据需要添加任意行,就像添加行一样VARIABLE_NAME="A_VALUE"
Flavien Volken 19'Aug

9

更简单:

  1. 抓取文件内容
  2. 删除所有空白行(以防万一您分隔了一些东西)
  3. 删除任何评论(以防万一您添加了一些...)
  4. 添加export到所有行
  5. eval 整个东西

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); }

(请source ~/.bash_profile确保您重新加载了BASH设置!!!或..只需新建一个标签页/窗口并解决问题即可),您这样称呼它:source_envfile .env


2
我不得不读gitlab秘密变量.ENV文本管道:根据您的解决了这个命令为我工作:source <( echo $(sed -E -n 's/[^#]+/ &/ p' <(echo "${2}" | tr -d '\r')) );。gitlab以某种方式用Windows回车符保存秘密变量,因此我不得不用来修剪它tr -d '\r'
metanerd

6

您可以使用原始脚本来设置变量,但是您需要通过以下方式(带有独立的点)来调用它:

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

与大多数其他答案不同,此解决方案不test.conf等同于脚本文件。这样会更好。除非您确实需要脚本,否则不要脚本是比较安全的,特别是如果有人不知道那是怎么回事(或忘记了)的话。
meustrus

5

在其他答案的基础上,这是仅导出文件中行的子集(包括带有空格的值)的方法PREFIX_ONE="a word"

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

5

这是我的变体:

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

与以前的解决方案相比:

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

您也可以内联运行(变量在当前的终端会话中公开): set -a && . ./.env && "$@" && echo "your comand here"
Giovanne Afonso,

4

.env在Mac上使用docker-compose和files,并想将导入.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.


2

我对较早提出的解决方案有疑问:

  • @anubhava的解决方案使编写bash友好的配置文件变得非常烦人,而且-您可能不希望始终导出配置。
  • 当您使用带空格或其他字符的变量在加引号的值中效果很好时,@ Silas Paul解决方案就会中断$()

这是我的解决方案,它仍然是非常糟糕的IMO-并不能解决Silas解决的“仅导出到一个孩子”问题(尽管您可以在子shell中运行它来限制范围):

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

2

我的要求是:

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

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

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

1
如果您将“ -o allexport”加在“ export”前面,有什么意义?
il--ya

2

首先,创建一个环境文件,该文件将具有如下所示环境的所有键-值对,并根据您的喜好命名该文件 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

1

当我尝试--env-file在外壳中重用Docker时遇到了这个线程。 它们的格式与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

1

值中的空格

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

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

我发现了2个解决方案,这些解决方案都支持空行和注释,并具有这样的值。

一个基于sed和@ javier-buzzi的答案

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

和一个基于@ john1024 答案的循环读取行

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

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


1
我必须修改sed解决方案才能使其正常工作。但是首先要有一些解释:-e是的缩写--expression,仅告诉您sed要执行什么操作。-e /^$/d从输出(而不是文件)中删除空行。-e /^#/d从输出中删除bash注释(以#开头的行)。's/.*/declare -x "&"/g'用替换(替代)剩余的行declare -x "ENV_VAR="VALUE""。当您获得此资源时,至少对我而言,它不起作用。相反,我不得不使用source <(sed -e /^$/d -e /^#/d -e 's/.*/declare -x &/g' .env)来删除多余的"包装。
jcasner '18

我不使用ENV_VAR="lorem ipsum",我有ENV_VAR=lorem ipsum,没有在.ENV文件的报价。现在我不确定为什么,但是这在解析该文件的其他工具中可能是有问题的。与其说lorem ipsum我以"lorem ipsum"价值结尾,不如说是引号。Thx的解释:)
Janusz Skonieczny

1
如果这是我的选择,则不会使用ENV_VAR="lorem ipsum"。在我的用例中,我的托管服务提供商基于我设置的一些配置选项生成此文件,并且它们插入双引号。因此,我被迫解决它。感谢您在这里的帮助-节省了我很多时间sed自己尝试制定正确的选项!
jcasner

1

尝试这样的事情

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

1
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每一个价值
  • 单线


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

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.