.env 文件学习笔记
什么是 .env 文件
.env
文件是一个配置文件,用于存储环境变量。主要用途:
- 存储敏感信息(数据库连接信息、API密钥、密码等)
- 环境配置(开发/生产环境标识、端口号等)
.env 文件命名约定
标准命名方式
-
默认名称:
.env
(推荐)- 最通用的命名方式
- 被所有工具和框架默认支持
- 无需额外配置
-
环境区分:
.env.{environment}
.env.development
.env.production
.env.testing
-
本地覆盖:
.env.local
- 用于本地开发环境的特定配置
- 通常会被加入到 .gitignore
自定义文件名
可以使用自定义文件名(如 mydemo.env
),但需要注意:
-
加载方式的变化
# Python from dotenv import load_dotenv load_dotenv('mydemo.env') # 需要明确指定文件名
// Node.js require('dotenv').config({ path: 'mydemo.env' })
-
注意事项
- 某些工具可能无法自动识别非标准命名的文件
- 需要在配置中显式指定文件路径
- 团队协作时需要额外的文档说明
- 可能需要修改构建工具和部署脚本的配置
-
建议
- 除非有特殊需求,建议使用标准的
.env
命名 - 如果使用自定义名称,在项目文档中清晰说明
- 确保所有团队成员了解非标准命名的原因和使用方法
- 除非有特殊需求,建议使用标准的
.env vs 系统环境变量
-
作用范围不同
.env
文件中的变量仅对特定应用程序有效- 系统环境变量对整个操作系统有效
-
生命周期不同
.env
变量只在应用程序运行时被加载和使用- 系统环境变量在系统运行期间一直存在
-
使用方式
.env
变量需要通过特定的库来读取(如 Node.js 的dotenv
、Python 的python-dotenv
)- 系统环境变量可以直接通过系统API或命令访问
变量优先级
不同编程语言和框架对环境变量的处理优先级可能不同。以下是常见语言的优先级说明:
Python 环境变量优先级
优先级 | 来源 | 说明 | 覆盖方式 |
---|---|---|---|
1 (最高) | 命令行参数 | 运行时通过命令行传入的环境变量 | python script.py ENV_VAR=value |
2 | 系统环境变量 | 操作系统级别的环境变量 | export ENV_VAR=value (Linux)SET ENV_VAR=value (Windows) |
3 | .env 文件 | 项目配置文件中的环境变量 | 修改 .env 文件内容 |
4 (最低) | 默认值 | 代码中硬编码的默认值 | 修改源代码 |
调整优先级:
# 让 .env 文件变量覆盖系统环境变量
from dotenv import load_dotenv
load_dotenv(override=True)
# 使用 os.environ 强制设置
import os
os.environ['ENV_VAR'] = 'value' # 最高优先级
Node.js 环境变量优先级
优先级 | 来源 | 说明 | 覆盖方式 |
---|---|---|---|
1 (最高) | 命令行参数 | 通过 CLI 传入的环境变量 | ENV_VAR=value node app.js |
2 | process.env 直接设置 | 代码中直接设置的环境变量 | process.env.ENV_VAR = 'value' |
3 | 系统环境变量 | 操作系统级别的环境变量 | 系统环境变量设置 |
4 | .env.local | 本地特定的环境变量 | 修改 .env.local 文件 |
5 | .env | 默认环境变量文件 | 修改 .env 文件 |
6 (最低) | 默认值 | 代码中的默认值 | 修改源代码 |
调整优先级:
// 自定义 dotenv 配置
require('dotenv').config({
override: true, // 覆盖已存在的环境变量
path: '/custom/path/.env' // 自定义路径
})
Docker 环境变量优先级
优先级 | 来源 | 说明 | 示例 |
---|---|---|---|
1 (最高) | 运行时参数 | docker run 的 -e 参数 | docker run -e ENV_VAR=value |
2 | docker-compose.override.yml | 覆盖文件中的环境变量 | environment: 部分 |
3 | docker-compose.yml | 主配置文件中的环境变量 | environment: 部分 |
4 | .env 文件 | Docker Compose 环境文件 | ENV_VAR=value |
5 | Dockerfile | Dockerfile中的ENV指令 | ENV ENV_VAR=value |
6 (最低) | 默认值 | 镜像中的默认环境变量 | 基础镜像定义 |
最佳实践建议:
-
开发环境:
- 使用 .env 文件存储开发配置
- 使用 .env.local 存储个人特定配置(加入 .gitignore)
-
生产环境:
- 优先使用平台提供的环境变量管理
- 避免使用 .env 文件存储敏感信息
- 使用密钥管理服务
-
CI/CD环境:
- 使用CI/CD平台的环境变量功能
- 构建时注入必要的环境变量
-
调试技巧:
- 使用环境变量打印工具查看实际值
- 注意变量覆盖情况
# Python import os print(dict(os.environ))
// Node.js console.log(process.env)
最佳实践
.env
文件应该添加到.gitignore
中,避免提交到版本控制系统- 提供
.env.example
作为模板 - 避免在
.env
文件中定义系统关键的环境变量 - 使用独特的前缀命名环境变量(如 APP_、MY_APP_ 等)
.env 文件格式要求
- 基本格式:每行一个变量,使用
KEY=VALUE
格式 - 注释:使用
#
开头的行为注释 - 引号使用:
- 值可以不使用引号:
DB_HOST=localhost
- 也可以使用单引号或双引号:
DB_HOST='localhost'
或DB_HOST="localhost"
- 如果值包含空格,必须使用引号:
APP_NAME="My Application"
- 值可以不使用引号:
- 多行值:
- 可以使用引号包裹多行值
- 也可以使用
\
进行换行
- 空格规则:
- 等号两边不应该有空格:
KEY=value
(推荐)而不是KEY = value
- 值中的空格会被保留
- 等号两边不应该有空格:
- 特殊字符:
- 可以使用转义字符
\
- 支持常见的转义序列如
\n
、\t
等
- 可以使用转义字符
- 变量引用:
- 某些实现支持使用
$
引用其他变量:URL=https://$DOMAIN
- 建议使用
${VARIABLE}
格式以明确引用范围
- 某些实现支持使用
示例:
# 数据库配置
DB_HOST=localhost
DB_PORT=5432
DB_NAME="my_database"
DB_USER='admin'
DB_PASS="complex@password123"
# 应用配置
APP_NAME="My Cool App"
APP_ENV=development
API_KEY=abc123
# 多行值示例
CERT_PRIVATE=-----BEGIN RSA PRIVATE KEY-----\
MIIEpQIBAAKCAQEA\
...
-----END RSA PRIVATE KEY-----
# 变量引用示例
DOMAIN=example.com
URL=https://${DOMAIN}/api
环境变量设置方式对比
设置方式 | 语法示例 | 生效范围 | 持久性 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|---|---|
.env 文件 | DB_HOST=localhost | 项目级 | 项目运行时 | • 易于版本控制 • 支持注释和文档 • 支持复杂值格式 • 方便环境切换 | • 需要额外库支持 • 仅对特定应用有效 • 需注意文件安全 | • 开发环境 • 团队协作项目 |
Windows SET | SET DB_HOST=localhost | 当前命令行 | 会话期间 | • 简单直接 • 无需依赖 • 立即生效 | • 窗口关闭失效 • 不支持复杂格式 • 难以批量管理 | • 临时测试 • 快速验证 |
Windows SETX | SETX DB_HOST "localhost" | 系统级 | 永久 | • 永久保存 • 全局有效 • 无需依赖 | • 需重启生效 • 长度限制1024字符 • 可能需管理员权限 | • 系统级配置 • 全局设置 |
Linux export | export DB_HOST=localhost | 当前shell | 会话期间 | • 简单直观 • 立即生效 • 可用于脚本 | • 会话结束失效 • 不支持复杂格式 • 无注释功能 | • 临时测试 • Shell脚本 |
Linux系统配置 (/etc/environment) | DB_HOST=localhost | 系统级 | 永久 | • 系统级永久 • 全局有效 • 所有用户可用 | • 需重新登录 • 需root权限 • 格式简单 | • 系统级配置 • 多用户环境 |
Docker ENV | ENV DB_HOST=localhost 或 docker run -e DB_HOST=localhost | 容器级 | 容器生命周期 | • 容器级隔离 • 支持运行时覆盖 • 适合容器部署 | • 仅用于Docker • 注意镜像安全 • 批量管理繁琐 | • 容器化部署 • 微服务架构 |
场景选择建议
使用场景 | 推荐方式 | 原因 |
---|---|---|
开发环境 | .env 文件 | 便于团队协作,环境一致性管理 |
生产环境 | • Docker环境变量 (容器化) • 系统环境变量 (传统部署) • 云平台环境变量 | 根据部署方式选择最适合的方案 |
临时测试 | SET/export 命令 | 简单快速,即用即弃 |
CI/CD环境 | CI/CD平台环境变量 | 安全性好,便于管理 |
常见使用模式
多环境配置
# .env.development
APP_ENV=development
DEBUG=true
API_URL=http://dev-api.example.com
# .env.production
APP_ENV=production
DEBUG=false
API_URL=https://api.example.com
# .env.testing
APP_ENV=testing
DEBUG=true
API_URL=http://test-api.example.com
环境变量命名约定
- 全大写字母:
DATABASE_URL
,API_KEY
- 使用下划线分隔:
MYSQL_ROOT_PASSWORD
- 使用前缀区分应用:
MYAPP_DATABASE_URL
- 使用前缀区分环境:
DEV_API_URL
,PROD_API_URL
- 布尔值使用标准命名:
IS_DEBUG
,HAS_CACHE
,ENABLE_FEATURE
安全性考虑
敏感信息保护
-
永远不要提交 .env 文件到版本控制
# .gitignore .env .env.* !.env.example
-
加密敏感值
- 考虑使用加密工具加密敏感值
- 使用密钥管理服务(如AWS KMS、HashiCorp Vault)
-
访问控制
- 限制 .env 文件的文件系统权限
- 在生产环境使用密钥管理服务而不是 .env 文件
最佳安全实践
- 定期轮换敏感信息(密码、密钥等)
- 使用足够长度和复杂度的密钥
- 不同环境使用不同的密钥
- 避免在日志中打印环境变量值
- 在容器化环境中注意镜像安全
常见问题与解决方案
1. 环境变量未加载
症状:
- 程序无法读取环境变量
- 环境变量值为空
解决方案:
- 检查文件名是否正确(
.env
而不是env
) - 确认文件位置在项目根目录
- 验证加载代码是否正确:
# Python from dotenv import load_dotenv load_dotenv() # 确保这行代码在使用环境变量之前
// Node.js require('dotenv').config()
2. 变量优先级冲突
症状:
- 环境变量值不符合预期
- 被其他来源的值覆盖
解决方案:
- 了解不同来源的优先级:
- 系统环境变量
- 命令行参数
- .env 文件
- 使用适当的加载选项:
# Python - 强制 .env 覆盖系统环境变量 load_dotenv(override=True)
3. 特殊字符处理
症状:
- 包含特殊字符的值被错误解析
- 多行值格式错误
解决方案:
- 使用引号包裹值
- 正确使用转义字符
- 多行值使用适当的语法:
# 使用引号和 \n CERT="line 1\nline 2" # 或使用反斜杠续行 CERT=line 1\ line 2
Python 环境变量使用指南
基础使用方法
-
安装依赖
pip install python-dotenv
-
创建和加载 .env 文件
from dotenv import load_dotenv import os # 加载 .env 文件 load_dotenv() # 默认加载当前目录的 .env 文件 # 或者指定路径 load_dotenv("/path/to/.env") # 强制覆盖已存在的环境变量 load_dotenv(override=True)
-
读取环境变量
import os # 基本读取 database_url = os.getenv('DATABASE_URL') # 带默认值的读取 debug_mode = os.getenv('DEBUG', 'False') # 必需变量(如果不存在会抛出异常) api_key = os.environ['API_KEY']
进阶使用技巧
-
类型转换
# 布尔值转换 DEBUG = os.getenv('DEBUG', 'False').lower() in ('true', '1', 't') # 整数转换 PORT = int(os.getenv('PORT', '5000')) # 列表转换 ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', '').split(',')
-
使用 Pydantic 进行环境变量管理
from pydantic_settings import BaseSettings class Settings(BaseSettings): DATABASE_URL: str API_KEY: str DEBUG: bool = False PORT: int = 5000 class Config: env_file = '.env' env_file_encoding = 'utf-8' # 使用 settings = Settings() print(settings.DATABASE_URL)
-
多环境配置
import os from dotenv import load_dotenv # 根据环境加载不同的配置文件 env = os.getenv('ENV', 'development') load_dotenv(f'.env.{env}') # 加载基础配置,然后用特定环境的配置覆盖 load_dotenv('.env') # 基础配置 load_dotenv(f'.env.{env}', override=True) # 环境特定配置
实用代码片段
-
配置类模式
class Config: def __init__(self): load_dotenv() self.database_url = os.getenv('DATABASE_URL') self.api_key = os.getenv('API_KEY') self.debug = os.getenv('DEBUG', 'False').lower() == 'true' def __repr__(self): return f"Config(debug={self.debug})" # 使用 config = Config()
-
环境变量验证
def validate_env_vars(): required_vars = ['DATABASE_URL', 'API_KEY', 'SECRET_KEY'] missing_vars = [] for var in required_vars: if not os.getenv(var): missing_vars.append(var) if missing_vars: raise Exception(f"Missing required environment variables: {', '.join(missing_vars)}") # 应用启动时验证 validate_env_vars()
-
敏感信息处理
class DatabaseConfig: def __init__(self): self.user = os.getenv('DB_USER') self.password = os.getenv('DB_PASS') self.host = os.getenv('DB_HOST') self.port = int(os.getenv('DB_PORT', '5432')) @property def url(self): return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}" def __repr__(self): # 不显示敏感信息 return f"DatabaseConfig(host={self.host}, port={self.port})"
常见使用场景
-
Web 应用配置
from flask import Flask from dotenv import load_dotenv load_dotenv() app = Flask(__name__) app.config['SECRET_KEY'] = os.getenv('SECRET_KEY') app.config['DEBUG'] = os.getenv('DEBUG', 'False').lower() == 'true'
-
数据库连接
import psycopg2 from dotenv import load_dotenv load_dotenv() def get_db_connection(): return psycopg2.connect( host=os.getenv('DB_HOST'), database=os.getenv('DB_NAME'), user=os.getenv('DB_USER'), password=os.getenv('DB_PASS') )
-
API 客户端配置
import requests from dotenv import load_dotenv load_dotenv() class APIClient: def __init__(self): self.base_url = os.getenv('API_BASE_URL') self.api_key = os.getenv('API_KEY') def get_data(self, endpoint): headers = {'Authorization': f'Bearer {self.api_key}'} response = requests.get(f"{self.base_url}{endpoint}", headers=headers) return response.json()
调试技巧
-
打印所有环境变量
def print_env_vars(): import json # 获取所有环境变量 env_vars = dict(os.environ) # 过滤掉敏感信息 sensitive_keys = ['PASSWORD', 'SECRET', 'KEY'] for key in env_vars: if any(s in key.upper() for s in sensitive_keys): env_vars[key] = '***' # 格式化打印 print(json.dumps(env_vars, indent=2))
-
环境变量加载检查
def check_env_loading(): load_dotenv() print("Environment variables loaded from .env file") print(f"Current environment: {os.getenv('ENV', 'development')}") print(f"Debug mode: {os.getenv('DEBUG', False)}")