从环境文件中读取环境变量

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 参数覆盖。

【讨论】:

以上是关于从环境文件中读取环境变量的主要内容,如果未能解决你的问题,请参考以下文章

Flask配置文件和环境变量:配置文件和环境变量的读取与调用

SpringMVC配置文件中读取环境变量

从 logback 配置文件中读取环境变量

可以通过从文件中读取一次将环境变量存储为全局变量吗?

Symfony 4控制台命令不读取环境变量

在 Laravel 5.1 配置文件中读取 Vhosts 中设置的环境变量