从环境文件中读取环境变量
Posted
技术标签:
【中文标题】从环境文件中读取环境变量【英文标题】:Reading in environment variables from an environment file 【发布时间】:2017-03-06 02:33:14 【问题描述】:我想在本地环境中运行通常在 Docker 容器中运行的 Python 脚本。 docker-compose.yml
指定了一个 env_file,它看起来(部分)如下所示:
DB_ADDR=rethinkdb
DB_PORT=28015
DB_NAME=ipercron
为了在本地运行,我希望将这些行转换为
os.environ['DB_ADDR'] = 'rethinkdb'
os.environ['DB_PORT'] = '28015'
os.environ['DB_NAME'] = 'ipercron'
我可以编写我的解析器,但我想知道是否有任何现有的模块/工具可以从配置文件中读取环境变量?
【问题讨论】:
【参考方案1】:我使用Python Dotenv Library。只需安装库pip install python-dotenv
,使用您的环境变量创建一个.env
文件,然后在您的代码中导入环境变量,如下所示:
import os
from dotenv import load_dotenv
load_dotenv()
MY_ENV_VAR = os.getenv('MY_ENV_VAR')
来自.env
文件:
MY_ENV_VAR="This is my env var content."
当我需要在我的 docker 系统之外测试代码并准备将其再次返回到 docker 时,我会这样做。
【讨论】:
我也使用dotenv
。当您来自 JS 后端环境时,它们之间有很多相似之处,以至于学习曲线几乎是平坦的!
如果我在.bashrc
文件中有MY_ENV_VAR=""
,这不适用并返回空字符串;这是正常行为吗?
@alper 是的。如果您使用load_dotenv(override=True)
,它只会覆盖已在.bashrc
中设置的环境变量,如GitHub readme file 中所述。
通常在 .env 文件中,变量不需要引号。
@DanielLavedoniodeLima load_dotenv(override=True)
是否优先于 load_dotenv()
?【参考方案2】:
如果您的系统/环境/工作流程支持使用 shell 脚本,您可以创建一个包含这 2 个操作的脚本:
-
Sourcing the .env file and exporting them as environment variables
使用
set -a
选项,其中“创建或修改的每个变量或函数都具有导出属性并标记为导出到后续命令的环境”。
调用具有纯 os.environ.get
代码的 Python 脚本/应用程序
.env 文件示例(config.env):
TYPE=prod
PORT=5000
示例 Python 代码 (test.py):
import os
print(os.environ.get('TYPE'))
print(os.environ.get('PORT'))
示例 bash 脚本 (run.sh):
#!/usr/local/bin/bash
set -a
source config.env
set +a
python3 test.py
示例运行:
$ tree
.
├── config.env
├── run.sh
└── test.py
$ echo $TYPE
$ echo $PORT
$ python3 test.py
None
None
$ ./run.sh
prod
5000
当您直接运行 Python 脚本 (python3 test.py
) 而不使用 source
-ing .env 文件时,所有 environ.get
调用都会返回 None
。
但是,当您将它包装在一个首先将 .env 文件加载到环境变量中,然后运行 Python 脚本的 shell 脚本中时,Python 脚本现在应该能够正确读取环境变量了。
与other popular answer相比,它不需要任何外部Python库。
【讨论】:
我认为这是一个真正的 linux 解决方案。 这正是我所需要的。set -a
技巧非常优雅,在许多其他场景中都会派上用场。谢谢!
请注意export MY_VAR="Hello!"
与dotenv
以及bash source
兼容。不错【参考方案3】:
这也对你有用:
env_vars = [] # or dict
with open(env_file) as f:
for line in f:
if line.startswith('#') or not line.strip():
continue
# if 'export' not in line:
# continue
# Remove leading `export `, if you have those
# then, split name / value pair
# key, value = line.replace('export ', '', 1).strip().split('=', 1)
key, value = line.strip().split('=', 1)
# os.environ[key] = value # Load to local environ
# env_vars[key] = value # Save to a dict, initialized env_vars =
env_vars.append('name': key, 'value': value) # Save to a list
print(env_vars)
在 cmets 中,您会找到几种不同的方法来保存环境变量以及一些解析选项,即去掉前面的 export
关键字。另一种方法是使用python-dotenv 库。干杯。
【讨论】:
将它们保存到dict
中会比保存到列表中更好吗? // +1 去掉前面的 export
关键字【参考方案4】:
您可以使用ConfigParser
。示例可以在here找到。
但是这个库希望您的key
=value
数据出现在某些[heading]
下。比如:
[mysqld]
user = mysql # Key with values
pid-file = /var/run/mysqld/mysqld.pid
skip-external-locking
old_passwords = 1
skip-bdb # Key without value
skip-innodb
【讨论】:
此解决方案的主要缺点是如果添加了标头,则带有source <file>
的shell无法解析文件。【参考方案5】:
Dewald Abrie 发布了一个很好的解决方案,这里稍作修改,忽略了折线 (\n)
def get_env_data_as_dict(path: str) -> dict:
with open(path, 'r') as f:
return dict(tuple(line.replace('\n', '').split('=')) for line
in f.readlines() if not line.startswith('#'))
print(get_env_data_as_dict('../db.env'))
【讨论】:
我认为这是一个很好的答案,干净,实用,并且没有外部依赖。我没有检查复杂的需求,但是在一个有本地 repo 并且需要提供所有代码但不提供变量内容(例如 A 令牌)的情况下非常方便,只需按照以下一般步骤操作:1)创建了一个 .env 文件,2)将它放在您的虚拟环境文件夹中,3)在 .gitignore 中包含 virtualenv 文件夹,4)在任何脚本中使用提供的函数读取变量,并且不会在 repo 中公开,而只是在您的本地机器。【参考方案6】:仅使用 python 标准
import re
envre = re.compile(r'''^([^\s=]+)=(?:[\s"']*)(.+?)(?:[\s"']*)$''')
result =
with open('/etc/os-release') as ins:
for line in ins:
match = envre.match(line)
if match is not None:
result[match.group(1)] = match.group(2)
【讨论】:
【参考方案7】:如何获得更紧凑的解决方案:
import os
with open('.docker-compose-env', 'r') as fh:
vars_dict = dict(
tuple(line.split('='))
for line in fh.readlines() if not line.startswith('#')
)
print(vars_dict)
os.environ.update(vars_dict)
【讨论】:
不错。您的解决方案将 \n 添加到该值,因为它是行尾。稍作调整:def get_env_data_as_dict(path: str) -> dict: with open(path, 'r') as f: return dict(tuple(line.replace('\n', '').split('=')) for lin in f.readlines() if not line.startswith('#'))
【参考方案8】:
在无法使用python-dotenv 的情况下,我使用了类似以下的方法:
import os
def load_env_file(dotenv_path, override=False):
with open(dotenv_path) as file_obj:
lines = file_obj.read().splitlines() # Removes \n from lines
dotenv_vars =
for line in lines:
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", maxsplit=1)
dotenv_vars.setdefault(key, value)
if override:
os.environ.update(dotenv_vars)
else:
for key, value in dotenv_vars.items():
os.environ.setdefault(key, value)
它读取给定的文件并解析其中包含“=”符号的行。 符号前面的值是键,后面的值是值。
与 env 文件中具有相同键的当前环境变量可以保持不变,也可以用 override
参数覆盖。
【讨论】:
以上是关于从环境文件中读取环境变量的主要内容,如果未能解决你的问题,请参考以下文章