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

Set environment variables from file of key/value pairs

提问人:hugo19941994 提问时间:10/12/2013 最后编辑:Francisco Pugahugo19941994 更新时间:8/1/2023 访问量:956006

问:

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

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

bash 环境变量

评论

0赞 Ciro Santilli OurBigBook.com 11/19/2014
在 unix 上也一样:unix.stackexchange.com/questions/31797/......
5赞 Dan Dascalescu 11/14/2018
这是一个很好的问题,但措辞过于具体,使用特定的变量名称(“MINIENTREGA_FECHALIMITE”?这是什么意思?)和数字(3)。一般问题很简单,“如何将一组键/值对从文本文件导出到 shell 环境中”。
2赞 Dan Dascalescu 12/25/2018
此外,这已经在 unix.SE 上得到了回答,可以说在那里更切合主题。
5赞 anubhava 9/25/2022
我对将这个问题标记为 4 年后出现的问题的欺骗持保留意见。
1赞 Charles Duffy 4/13/2023
@anubhava,这很公平,但这里的答案质量非常糟糕。首要目标是给人们提供良好的信息,不是吗?(关于这个问题有一些很好的答案,但与顶部充满错误的答案相比,它们的分数很低;这是 Stack Overflow 的格式和规则不能很好地为我们的社区/读者服务的地方)。

答:

368赞 anubhava 10/12/2013 #1

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

在文件本身中添加命令: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

评论

6赞 o11c 8/31/2017
如果它不是来自文件,请使用< <(commands that generate output)
15赞 heralight 10/28/2018
你有一个更干净的解决方案,我更喜欢set -o allexport
7赞 FilBot3 2/16/2019
如果在系统之间使用此 .env 文件,则插入 Java、SystemD 或其他工具会破坏它export
2赞 Shardj 3/31/2020
awk '{print "export " $0}' envfile方便的命令,用于将导出预置到每行的开头
3赞 Stokedout 4/7/2021
如果使用export . ./conf/prac1
1583赞 Silas Paul 1/4/2014 #2

这可能会有所帮助:

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

我使用它的原因是如果我想在我的 rails 控制台中测试东西。.env

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


从这个答案中,你可以用这个自动检测操作系统:

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

评论

50赞 manalang 4/26/2016
这是一个较短的变体eval $(cat .env) rails
1赞 Kzqai 12/16/2022
就我而言,这似乎不适用于 .env 文件中的多个条目,因为它不附带换行符,因此您将 SOME_ENV_VAR=sdfajkasldfjlasdkfj ANOTHERVAR=sdafjasdfkl 全部作为单个 var(因为它都是在单行导出之后)。
1赞 Nam G VU 1/6/2023
这太复杂了 - 请考虑以下更简单的方法set -o allexport
0赞 Player Schark 3/2/2023
在我的情况下,添加了一个\r,这导致了错误的值。我添加了,但也会起作用。最后一个命令是tr -d '\r'tr '\r' '\0'export $(grep -v '^#' .env | tr '\r' '\0' | xargs -d '\n')
2赞 Charles Duffy 4/13/2023
不,在 GNU 系统上是不正确的。未加引号的扩展仍然在空格上分词;xargs 发出 NUL 的事实并没有改变这一点。(某些版本的 bash 只是从命令替换结果中以静默方式删除 NULL,因此代码的确切行为与版本相关,因此无法可靠地进行测试)。就此而言,在任何地方都是不必要的,因为换行符存在于 IFS 中,因此在命令替换结果中,它的解析方式与空格完全相同。export $(grep -v '^#' .env | xargs -0)xargs -d '\n'
8赞 Extrapolator 2/17/2014 #3

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

. ./minientrega.sh

此外,方法可能存在问题。我建议使用这种方法。cat | while readwhile 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

评论

0赞 meustrus 5/5/2020
与大多数其他答案不同,此解决方案不会作为脚本文件进行评估。这让它变得更好。除非您确实需要,否则不允许编写脚本会更安全,尤其是当有人没有意识到这是正在发生的事情(或忘记)时。test.conf
5赞 Guss 1/28/2015 #4

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

  • @anubhava的解决方案使编写 bash 友好的配置文件变得非常烦人,而且 - 您可能不想总是导出您的配置。
  • @Silas Paul 解决方案中断时,您的变量具有空格或其他字符,这些字符在引号值中效果很好,但会弄得一团糟。$()

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

source .conf-file
export $(cut -d= -f1 < .conf-file)
34赞 selvan 4/7/2015 #5
eval $(cat .env | sed 's/^/export /')

评论

1赞 Mario Uher 7/25/2015
使用允许您使用空行以获得更好的可读性。eval $(cat .env | sed 's/^[^$]/export /')
2赞 Owen S. 3/3/2017
我发现这剥离了初始字符。即对于我得到的文件.这更适合跳过空行:.cat .env | sed 's/^[^$]/export /'A=foo\nB=bar\nexport =foo\nexport =bar\ncat .env | sed '/^$/! s/^/export /'
0赞 Owen S. 3/3/2017
(我还注意到,为了 UNIX 代码高尔夫球手,无论哪种情况都不需要:同样有效。cateval $(sed 's/^/export /' .env)
0赞 stefcud 1/5/2022
不支持注释行的首字母#
0赞 Amanuel Nega 3/30/2022
评估sed 's/^/export /' .env
960赞 user4040650 6/22/2015 #6

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

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

评论

16赞 2upmedia 12/13/2022
或单行.感谢您@user4040650分享。set -o allexport && source conf-file && set +o allexport
5赞 Pål Thingbø 12/30/2022
这在脚本中有效
1赞 JP Zhang 11/13/2023
这行得通。甚至可以使用 $ 特殊字符进行确认。
17赞 jwfearn 9/10/2015 #7

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

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

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

set +o它本身以 bash 以后可以执行的格式输出所有当前选项。也很方便:它本身以人性化的格式输出您当前的所有选项。set -o

评论

2赞 b4hand 9/12/2015
如果您需要取消设置仅在 .exec env -i basheval.env
17赞 Jaydeep Solanki 10/22/2015 #8

改进 Silas Paul 的回答

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

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

评论

0赞 wacha 4/2/2020
然后,您可以使用它来获得采购的好处(例如,脚本可以执行)。(set -a; source dev.env; set +a; rails c)
52赞 gsf 12/5/2015 #9

该选项在此处的其他几个答案中提到,这是快捷方式。获取 .env 确实比遍历行和导出要好,因为它允许注释、空行,甚至命令生成的环境变量。我的 .bashrc 包括以下内容:allexportset -a

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

评论

5赞 Bastian Venthur 8/1/2017
这看起来不错,但是当您离开目录时会卸载环境变量吗?
1赞 gsf 8/2/2017
我不会取消设置变量,这从来都不是问题。我的应用程序倾向于使用不同的变量名称,如果存在重叠,我会在 .env 中将它们设置为空白。VAR=
0赞 Giovanni Cappellotto 12/21/2015 #10

如果由于其中一个变量包含包含空格的值而收到错误,您可以尝试重置 bash(内部字段分隔符)以让 bash 将结果解释为可执行文件的参数列表。IFS\ncat .envenv

例:

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

另请参阅:

27赞 tutuDajuju 4/7/2016 #11

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

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

我发现这在构建这样的文件以使用 EnvironmentFile 加载到 systemd 单元文件中时特别有用。

21赞 Nagev 3/8/2017 #12

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

如果脚本按指示调用: ,则 minientrega.sh 可能具有:minientrega.sh prac1

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

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

以下内容摘自集合文档

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

set [--abefhkmnptuvxBCEHPT] [-o option-name] [argument ...] set [+abefhkmnptuvxBCEHPT][+o 选项名称][参数...]

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

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

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

还有这个:

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

6赞 Victor Roetman 3/9/2017 #13

基于其他答案,这里有一种方法,可以仅导出文件中的行子集,包括带有空格的值,例如:PREFIX_ONE="a word"

set -a
. <(grep '^[ ]*PREFIX_' conf-file)
set +a
5赞 Tudor Ilisoi 5/26/2017 #14

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

1赞 Bob Zoller 6/24/2017 #15

当我尝试在 shell 中重用 Docker 时,我遇到了这个线程。它们的格式与 bash 不兼容,但很简单:,没有引用,没有替换。它们还会忽略空行和注释。--env-filename=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)
317赞 Dan Kowalczyk 8/31/2017 #16
set -a
. ./env.txt
set +a

如果像这样:env.txt

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

解释 -a 等同于 allexport。换言之,shell 中的每个变量赋值都会导出到环境中(供多个子进程使用)。更多信息可以在 Set builtin 文档中找到:

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

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

评论

0赞 Alexis 1/22/2022
如果有评论怎么办?和?VAR2=$VAR1
2赞 Dan Kowalczyk 1/25/2022
嗨,@Alexis。这里使用的命令本质上就像在终端上打字一样。您可以在终端上自己尝试一下,看看结果会是什么。注释将被忽略,对其他变量的引用将起作用,只要它们之前已经定义过。.
0赞 Alexis 1/25/2022
是的,我试过它以这种方式工作。感谢您的跟进!
11赞 Javier Buzzi 9/13/2017 #17

简单:

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

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

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

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_profilesource_envfile .env

评论

2赞 metanerd 11/24/2017
我必须从管道的 gitlab 秘密变量中读取 .env 文本: 根据您的解决方案,此命令对我有用:.不知何故,gitlab 使用 windows 回车符保存了秘密变量,所以我不得不用 .source <( echo $(sed -E -n 's/[^#]+/ &/ p' <(echo "${2}" | tr -d '\r')) );tr -d '\r'
4赞 Janusz Skonieczny 4/5/2018 #18

值中的空格

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

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)

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

评论

1赞 jcasner 4/11/2018
我必须修改解决方案才能使其正常工作。但首先要解释一下:是 的缩写,它只是告诉要执行什么操作。 从输出(而不是文件)中删除空行。 从输出中删除 bash 注释(以 # 开头的行)。 用 替换(替换)其余行。当你采购这个时,至少对我来说,它不起作用。相反,我不得不使用 ,来删除额外的包装器。sed-e--expressionsed-e /^$/d-e /^#/d's/.*/declare -x "&"/g'declare -x "ENV_VAR="VALUE""source <(sed -e /^$/d -e /^#/d -e 's/.*/declare -x &/g' .env)"
0赞 Janusz Skonieczny 4/11/2018
我不使用 , 我有 , 在 .env 文件中没有引号。现在我不确定为什么,但这在解析此文件的其他工具中可能是有问题的。而不是我以价值结束——以引号结束。谢谢解释:)ENV_VAR="lorem ipsum"ENV_VAR=lorem ipsumlorem ipsum"lorem ipsum"
1赞 jcasner 4/13/2018
如果这是我的选择,我也不会使用。在我的用例中,我的托管服务提供商根据我设置的一些配置选项生成此文件,并插入双引号。所以,我被迫解决它。感谢您在这里的帮助 - 为我节省了很多时间,试图自己制定正确的选项!ENV_VAR="lorem ipsum"sed
6赞 Alex 4/18/2019 #19

我的要求是:

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

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

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

评论

3赞 il--ya 7/24/2019
如果你在它们前面加上“export”,那么“-o allexport”有什么意义呢?
4赞 Anand Tripathi 5/20/2019 #20

首先,创建一个环境文件,该文件将包含环境的所有键值对,如下所示,并按照您喜欢的任何名称命名(在我的情况下是它的)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
8赞 Nick Grealy 5/21/2019 #21

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

.env

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

溶液

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

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

Bash 版本

$ /bin/bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.
22赞 Flavien Volken 5/28/2019 #22

我发现的最短方法:

您的文件:.env

VARIABLE_NAME="A_VALUE"

然后就

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

奖励:因为它是一个简短的单行代码,所以它在文件中非常有用package.json

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

注意:这种方式不会将变量导出到子进程,如果需要,请检查其他答案。

评论

0赞 Matteo 8/21/2019
如果你有很多变量怎么办?
0赞 Flavien Volken 8/26/2019
@Madeo您可以根据需要添加任意数量的行,与行相同VARIABLE_NAME="A_VALUE"
0赞 colllin 1/28/2021
警告:这会将这些变量导出到您的活动 shell...
3赞 Flavien Volken 1/28/2021
是的,是 的同义词,因此也是一样的.source
0赞 Konrad Rudolph 3/8/2023
这没有定义环境变量,这就是这个问题的意义所在。
1赞 gaozhidf 11/26/2019 #23

尝试这样的事情

for line in `cat your_env_file`; do if [[ $line != \#* ]];then export $line; fi;done
1赞 Ricardo Yanez 12/19/2019 #24

我的 .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')
8赞 Alex Skrypnyk 1/21/2020 #25
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赞 René Pardon 2/17/2021
我真的很感谢你的解决方案。在大多数项目中,您都有带有注释、空格、没有导出语句等的 .env 文件。
15赞 glen 1/22/2020 #26

这是我的变体:

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

与以前的解决方案相比:

  • 它不会泄漏作用域之外的变量(来自的值不会暴露给调用方).env
  • 不 clobber 选项set
  • 返回已执行命令的退出代码
  • 使用POSIX兼容set -a
  • 使用而不是避免 bashism.source
  • 如果加载失败,则不调用命令.env
with_env rails console

评论

0赞 Giovanne Afonso 4/18/2020
您也可以内联运行(变量向当前终端会话公开):set -a && . ./.env && "$@" && echo "your comand here"
107赞 Huan 2/26/2020 #27

我发现最有效的方法是:

export $(xargs < .env)

解释

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

key=val
foo=bar

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

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

限度

  1. 它不处理值中包含空格的情况。env 等命令会生成此格式。– @Shardj

评论

1赞 alexwenzel 1/31/2023
对我来说是完美的解决方案。
0赞 PatB 4/19/2020 #28

这个处理 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
22赞 Jules Colle 8/6/2020 #29

不太清楚为什么,或者我错过了什么,但在经历了大部分答案并失败了之后。我意识到使用这个.env文件:

MY_VAR="hello there!"
MY_OTHER_VAR=123

我可以简单地这样做:

source .env
echo $MY_VAR

输出:Hello there!

似乎在 Ubuntu linux 中工作得很好。

评论

0赞 kolypto 2/9/2021
如果您在 Docker 中使用此类 env 文件,则将包含引号作为值的一部分:)docs.docker.com/compose/env-fileMY_VAR
1赞 questionto42 1/19/2022
@kolypto 同样的情况也会发生在投票率较高的答案的任何其他命令上。这只是示例的选择。这只是为了表明你也可以只是采购它 - 是核心思想。其余的技巧是,例如,也覆盖特殊标志。
0赞 Edgar Froes 1/12/2023
也适用于Mac。
1赞 Konrad Rudolph 3/8/2023
您缺少的是,这不会定义任何环境变量。它定义参数。它们完全不同:您调用的任何命令都不会看到这些值,因为参数不是继承的。
5赞 Amir 8/10/2020 #30

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

  • 忽略注释行
  • 允许值中的空格
  • 允许空行
  • 能够在默认为 .env 时传递自定义 env 文件
  • 允许导出以及内联运行命令
  • 如果 env 文件不存在,则退出
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}")
}

将函数保存到.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

受到哈维尔和其他一些评论的启发。

4赞 Polv 8/21/2020 #31

修改自 @Dan Kowalczyk

我把它放进去.~/.bashrc

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

与 Oh-my-Zsh 的 dotenv 插件交叉兼容非常好。(有 Oh-my-bash,但它没有 dotenv 插件。

-1赞 Yuki Inoue 9/14/2020 #32

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

用法

dotenv_export是一个小型实用程序命令,它读取文件并使用 Ruby Dotenv 实现将其转换为语句。.envexport

# first install `dotenv_export`
gem install dotenv_export

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

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

评论

6赞 dtoux 4/26/2021
这太愚蠢了 - 拉取 250MB+ 的依赖项,用于在过去 40+ 年中开发的每个 shell 中用两行代码完成的事情:-) ...但你可以做得更糟,你可以把Docker带入这个;-)
4赞 Iwan Aucamp 9/15/2020 #33

我用这个:

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'

然后将 equal 替换为 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

然后最终采购所有这些。

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

3赞 Lucas B 12/3/2020 #34

如何保存变量:

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

我如何加载它们

source myvariables.sh
13赞 gogo 12/31/2020 #35

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

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

.env文件示例:

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

测试:

env -S "$(cat .env)" sh -c 'echo "$KEY"'
41赞 kolypto 2/9/2021 #36

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

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=ba='b'

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

若要调试此代码,请替换为,你将看到此命令生成的内容。sourcecat


direnv 用户请注意:它有一个辅助功能,请在您的文件中使用它:dotenv.envrc

[ -f ".env" ] && dotenv ".env"

评论

1赞 Klaas van Schelven 2/19/2021
在 bash 上为我工作 (TM),使用以下烦人的字符串:FOO=~'#$&*()\|[=]{};'"<>/?!
1赞 takilara 5/12/2022
这几乎对我有用,但必须用 [[:space:]] 换掉 \s 才能使其也适用于 FreeBSD/Mac 上的 bash: ''' source <(cat .env | sed -e '/^#/d;/^[[:space:]]*$/d' -e “s/'/'\\\''/g” -e “s/=(.*)/='\1'/g”) '''
2赞 Lars Ejaas 10/19/2022
对我有用,尽管我不得不用以下命令替换最后一个 sed:转义等号周围的任何空格sed -e '/^#/d;/^\s*$/d' -e "s/'/'\\\''/g" -e "s/\ *=\ */=/g")
1赞 cody.codes 7/22/2023
添加到您的点文件中的完美小金块 - 当我的 .env 中有分号时,这是一种享受!🙏🏼
0赞 dtoux 4/26/2021 #37

如果您打算将脚本的最后一个命令作为脚本的最后一个命令,则可以使用解释器进行其他选择。这是脚本最后一行的样子:execexeclineb

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

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

#!/bin/execlineb
...
envfile -I /etc/default/bla
envfile /etc/default/bla-bla
my_cmd
14赞 Koichi Nakashima 5/2/2021 #38

使用 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)

评论

0赞 Pierre Gramme 1/4/2022
很棒的工具,非常注重细节。谢谢!!
0赞 F. Hauri - Give Up GitHub 1/9/2022
@PierreGramme 使用运行至少两个分支的专用工具来解决理解一个命令中简化的概念的问题似乎有点矫枉过正!
3赞 Pierre Gramme 1/10/2022
@FHauri 也许有点矫枉过正,但这个问题有 43 个不同的答案:这真的是一个这么简单的问题吗?在我的用例中,我有一个用 Python 方言编写的 .env 文件并将其应用于 Bash。由于管理空间等的惯例不同,不能简单地使用。这个工具及其对差异的分析对我来说绝对有用source
0赞 intentionally-left-nil 7/21/2021 #39

我对此的贡献是扩展了@的答案 user4040650 以允许在 Git 存储库中轻松使用。它将尝试从当前目录获取 .env 文件,或者如果不存在,则从您所在的 git 存储库中获取 .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
}
1赞 Tobias Bergkvist 9/2/2021 #40

符合 POSIX 的解决方案(不依赖于 bash)

正如其他人所指出的,在这里使用 for/while 循环的问题在于变量在 shell 及其子 shell 之间不共享。然而,我们可以做的是使用 args/stdin/stdout 在 shell 之间传递文本。

当我们获取脚本时,在子 shell 中设置环境变量将无济于事

变量不会传播回去 - 但我们知道我们可以发送文本。这个文本也可以是代码,我们可以在当前的 shell 中使用 .eval

如果我们生成用于设置所有环境变量的代码,然后对结果进行评估,会怎么样?

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

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

bash 中的这种函数式元编程可以非常灵活。您也可以通过这种方式生成 bash/sh 以外的其他语言

5赞 lapsus63 10/22/2021 #41

我的版本:

我打印文件,删除注释行、空行,并从“=”符号拆分键/值。然后我只应用导出命令。

此解决方案的优点是文件可以在值中包含特殊字符,例如管道、html 标记等,并且值不必像实际属性文件那样用引号括起来。

# 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

3赞 warvariuc 12/24/2021 #42
sh -ac '. conf-file; yourcommand'

开关导出所有变量,以便它们可供程序使用。-a

与较长的版本不同,使用可确保导出的值不会永久污染当前环境。它只为在子 shell 中运行的程序获取和导出变量。set -a; . conf-file; set +a; yourcommandsh

1赞 F. Hauri - Give Up GitHub 1/8/2022 #43

export就是答案

自己进行互动练习

由于 交互式的,因此您可以尝试几乎所有内联内容!

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

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

注意双引号!

source别名.

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

还行!那么现在呢?export

export命令告诉 shell 将他的变量导出环境...因此,您必须先导出脚本变量,然后才能将它们用于任何子进程(如 、 ,甚至是另一个脚本rubypythonperlshell

清理以前的操作以进行进一步的演示

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

因此,在交互式 shell 中,尝试此操作的最简单方法是运行另一个

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

这只是打印一个空行,但如果你导出变量:

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

用于导出变量的示例 包装器

最小的包装器,没有安全问题(在采购可由其他用户编辑的脚本时要小心!!

#!/bin/sh

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

busybox sh -c 'set | grep MINIENTREGA'

使用 as 参数运行,应产生:prac1

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

在罚款中

  • 获取配置文件与声明变量相同。

  • 导出变量是指示 shell 在全局环境中为任何子进程共享变量。

这两个操作可以按任何顺序无动于衷地完成。唯一的要求是在尝试运行任何子进程之前完成这两个操作。

您甚至可以通过在配置文件导出来同时执行这两个操作,例如:

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

您甚至可以在一次操作中编写以下内容:

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

现在有一些抨击

确实提供了很多用于变量操作的工具

使用变量名称:nameref

#!/bin/bash

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

然后,使用 ,您可以使用以下命令显示任何变量的属性和内容:declare -p

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

变量名称前缀匹配进行扩展:

这是一个很好的工具!从手册页:

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

在上一个示例的实践中,使用可以写成:declare -p

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

当然,这可以用于以前定义的变量:export

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

export "${!MINIENTREGA_@}"

取消导出变量:

不幸的是,没有命令。为此,您可以使用 export 命令或 sign 的标志,而不是 for 命令:unexport-n+-declare

export -n MINIENTREGA_DESTINO
declare +x MINIENTREGA_FICHEROS

这两个命令都将取消设置变量的导出 flog

导出数组或关联数组

由于 数组和关联数组bashism,因此它们无法在 POSIX 环境中导出:

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

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

这只是打印一个空行!!当旗帜在场时!x

为此,一种解决方法是使用带有标志的 --rcfile-i

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

或者,如果使用交互式可能会导致问题,则可以连接脚本:

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

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

甚至

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

如果文件不可执行,例如示例:

bash -c "$(declare -p "${!my@}");. <(echo 'echo ${myavar[1]} ${myAvar[bar]}')"
2 2
2赞 vstelmakh 3/21/2022 #44

我以一个基于+方法的解决方案结束。这里的主要思想是防止覆盖现有变量allexportsource

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

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

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

# Usage example:
load_env_file "path/to/.env"
0赞 HoseinGhanbari 8/8/2022 #45
export $(grep -v '^#' envfilename | xargs -L 1  -d '\r' -d '\r\n')

这在 CentOS 上就像一个魅力;当你遇到被追加到加载的变量的问题时。此外,它还负责注释和空格。\r

2赞 kovan 9/3/2022 #46

如果你愿意,我的看法是保持全局环境变量空间不变,我认为这是可取的。

创建一个脚本,如下所示:

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

然后像这样使用:

dotenv env-file my-binary
0赞 Michaël 10/3/2022 #47

一种方法是即时创建一个文件,该文件位于每行的开头,将其放在子 shell 中,然后执行您的命令:zshexport

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

$
2赞 CallMarl 10/6/2022 #48

我构建此脚本以动态方式获取 env vars。

我之所以使用这个脚本,是因为我不想记住我在项目中使用的每个变量的名称,也不想将导出命令存储在历史记录中,也不希望将完整文件导出到 git 中。.env

#!/bin/sh

filename=".secret"

secret_var () {
    # Parametter 1 : Environnement vars anme

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

public_var () {
    # Parametter 1 : Environnement vars anme

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

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

它非常易于使用:

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

例:

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

当然,如果您想要存储值,则可以直接使用。exportpublic_var

10赞 Tilman Vogel 1/13/2023 #49

很抱歉添加另一个答案,但由于它很简单并且在许多情况下都有效,请尝试:

export $(< ~/my/.env)
3赞 Masih Jahangiri 4/26/2023 #50

一些注意事项:

  1. “.env”文件应具有“LF”行尾序列。
  2. 避免在环境变量中使用动态值,例如variable1=$variable2@$variable3
  3. 避免在环境变量 vavlue 中使用引号 (),例如"variable="value"

这是最好和最简短的答案

source .env && export $(cut -d= -f1 < .env)