【问题标题】: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'

我可以编写我的解析器,但我想知道是否有任何现有的模块/工具可以从配置文件中读取环境变量?

【问题讨论】:

    标签: python docker


    【解决方案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 个操作的脚本:

    1. Sourcing the .env file and exporting them as environment variables
      • 使用set -a 选项,其中“创建或修改的每个变量或函数都具有导出属性并标记为导出到后续命令的环境”。
    2. 调用具有纯 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 参数覆盖。

      【讨论】:

        猜你喜欢
        • 2019-11-27
        • 1970-01-01
        • 2012-09-05
        • 2017-11-16
        • 1970-01-01
        • 1970-01-01
        • 2010-12-30
        • 2020-09-02
        • 2013-05-27
        相关资源
        最近更新 更多