017、环境变量管理settings.json 中的 env 配置、shell 继承与平台差异处理一个让我熬夜的 bug上周五晚上十一点生产环境的 Claude Code 任务突然全部报错。日志里只有一行API_KEY not found。我第一反应是环境变量丢了。但检查了 .env 文件确认存在重启了容器问题依旧。最后发现是 settings.json 里env字段的配置覆盖了 shell 继承的环境变量而我在 Windows 开发机上测试时一切正常因为 Windows 的 env 继承机制和 Linux 不一样。这个坑让我意识到环境变量管理在 Claude Code 工程化里不是“写个 .env 就完事”那么简单。settings.json 的env配置、shell 继承行为、平台差异——这三者交织在一起稍不留神就会在跨环境部署时翻车。settings.json 中的 env 配置显式覆盖与隐式陷阱Claude Code 的 settings.json 支持在env字段里直接定义环境变量。写法很直观{env:{OPENAI_API_KEY:sk-xxx,LOG_LEVEL:debug,WORKER_COUNT:4}}这里踩过坑env字段的值必须是字符串。如果你写成数字4或者布尔值trueClaude Code 会静默忽略不会报错但变量就是没生效。我习惯在 CI 脚本里加一行检查# 别这样写直接依赖 settings.json 里的值# 应该先验证if[-z$WORKER_COUNT];thenechoWORKER_COUNT not set, defaulting to 2exportWORKER_COUNT2fi另一个容易忽略的点env字段的优先级高于系统环境变量但低于 shell 中显式 export 的变量。这意味着如果你在启动 Claude Code 之前已经export OPENAI_API_KEYsk-yyysettings.json 里的值不会覆盖它。这个行为在文档里没写清楚我是通过 strace 跟踪进程环境变量才确认的。shell 继承你以为你继承了其实没有Claude Code 启动时会继承父 shell 的环境变量。但这里有个关键细节继承的是启动时刻的快照不是动态引用。举个例子你在终端里exportMY_VARhelloclaude-code start# 此时 MY_VARhello 被继承exportMY_VARworld# Claude Code 进程里的 MY_VAR 仍然是 hello这个行为在单机开发时问题不大但在容器化部署或 CI/CD 流水线里就麻烦了。比如你在 Dockerfile 里ENV MY_VARhello然后在 entrypoint 脚本里修改了MY_VAR但 Claude Code 启动时拿到的还是 Dockerfile 里的值。我踩过的具体场景在 Kubernetes 的 preStop hook 里修改环境变量期望 Claude Code 的 worker 进程感知到变化。结果发现 worker 进程根本不知道外面变了因为环境变量是进程启动时拷贝的不是共享内存。解决方案用文件或信号机制来传递动态配置别指望环境变量能实时更新。# 别这样写依赖环境变量动态变化# 应该用文件监听# 在 Claude Code 的 settings.json 里配置{env:{CONFIG_FILE:/etc/claude/config.json}}# 然后在代码里监听文件变化平台差异Windows 的坑比想象的多跨平台部署时环境变量的行为差异是最大的坑。我整理了几个典型问题大小写敏感Windows 的环境变量名不区分大小写Linux 区分。你在 settings.json 里写api_key和API_KEY在 Windows 上会被视为同一个变量在 Linux 上是两个。这导致我在 Windows 开发机上测试通过的配置部署到 Linux 服务器上就找不到变量。路径分隔符Windows 用分号;Linux 用冒号:。如果你在env里配置PATH扩展必须根据平台写不同的值。我见过最离谱的 bugWindows 上PATH被错误地加了个冒号导致所有命令都找不到。变量展开Windows 的 cmd 和 PowerShell 对%VAR%和$VAR的处理不同。Claude Code 在 Windows 上启动时如果 settings.json 里写了HOME: %USERPROFILE%这个字符串不会被展开而是原样传递。正确的做法是用绝对路径。换行符Windows 的\r\n和 Linux 的\n会导致多行环境变量值被截断。如果你在 settings.json 里写了一个多行的 SSH 私钥Windows 上可能只读取了第一行。我的经验是在 settings.json 里只放平台无关的变量平台相关的变量通过 shell 包装脚本注入。# Windows 包装脚本 (start.bat)setCLAUDE_HOMEC:\claude claude-code start# Linux 包装脚本 (start.sh)exportCLAUDE_HOME/opt/claudeexecclaude-code start实战一个跨平台的环境变量管理方案经过多次踩坑我总结了一套相对稳定的方案核心思路是“分层管理、显式传递”。第一层系统环境变量。只放最基础的路径和标识比如CLAUDE_INSTALL_DIR、CLAUDE_DATA_DIR。这些在安装时设置之后基本不变。第二层settings.json 的 env 字段。放业务相关的配置比如 API key、日志级别、worker 数量。但注意不要放敏感信息。settings.json 可能被提交到版本控制或者被其他开发者看到。敏感信息应该用 secrets 管理工具。第三层shell 包装脚本。处理平台差异和动态配置。每个平台维护一个启动脚本在脚本里设置平台特定的环境变量然后调用 Claude Code。第四层运行时配置。通过文件或环境变量文件.env加载。Claude Code 支持--env-file参数可以指定一个 .env 文件。这个文件不会被 settings.json 覆盖适合存放动态生成的配置。具体实现# 启动脚本的核心逻辑# 1. 加载平台特定的环境变量source/etc/claude/platform.sh# 2. 加载 secrets从 vault 或 AWS Secrets ManagerexportAPI_KEY$(vaultreadsecret/claude/api_key)# 3. 生成 .env 文件包含动态配置cat/tmp/claude-runtime.envEOF WORKER_ID$(uuidgen)POD_NAME$HOSTNAMEEOF# 4. 启动 Claude Code显式指定 env 文件和 settingsclaude-code start\--settings/etc/claude/settings.json\--env-file /tmp/claude-runtime.env这样分层之后settings.json 只负责静态的、平台无关的配置平台差异和动态内容由脚本处理敏感信息由 secrets 管理。每个层次职责清晰排查问题时也能快速定位。个人经验性建议别在 settings.json 里写死路径。路径是平台差异的重灾区。用环境变量或相对路径然后在启动脚本里解析。每次修改 env 配置后重启 Claude Code 进程。不要相信“热加载”环境变量大部分实现都是启动时读取一次。在 CI 里加一个环境变量检查步骤。写个脚本遍历 settings.json 里引用的所有环境变量确认它们都存在。这个脚本帮我抓到了至少十次配置遗漏。Windows 开发Linux 部署的场景一定要在 Linux 上做集成测试。Windows 上跑得再顺到 Linux 上也可能因为大小写、路径分隔符、换行符等问题翻车。我吃过这个亏现在 CI 里强制跑 Linux 容器。敏感信息永远不要进 settings.json。哪怕你的仓库是私有的settings.json 也可能被不小心分享、备份、或者被 CI 日志打印出来。用 secrets 管理工具或者在启动时从环境变量读取。记录环境变量的来源。当环境变量冲突时知道哪个来源的优先级更高能省去大量排查时间。我习惯在日志里打印每个关键环境变量的来源settings.json / shell 继承 / .env 文件。环境变量管理看起来是小事但在工程化实践中它往往是跨环境部署的第一道坎。把这道坎迈过去后面的路会顺畅很多。
017、环境变量管理:settings.json 中的 env 配置、shell 继承与平台差异处理
017、环境变量管理settings.json 中的 env 配置、shell 继承与平台差异处理一个让我熬夜的 bug上周五晚上十一点生产环境的 Claude Code 任务突然全部报错。日志里只有一行API_KEY not found。我第一反应是环境变量丢了。但检查了 .env 文件确认存在重启了容器问题依旧。最后发现是 settings.json 里env字段的配置覆盖了 shell 继承的环境变量而我在 Windows 开发机上测试时一切正常因为 Windows 的 env 继承机制和 Linux 不一样。这个坑让我意识到环境变量管理在 Claude Code 工程化里不是“写个 .env 就完事”那么简单。settings.json 的env配置、shell 继承行为、平台差异——这三者交织在一起稍不留神就会在跨环境部署时翻车。settings.json 中的 env 配置显式覆盖与隐式陷阱Claude Code 的 settings.json 支持在env字段里直接定义环境变量。写法很直观{env:{OPENAI_API_KEY:sk-xxx,LOG_LEVEL:debug,WORKER_COUNT:4}}这里踩过坑env字段的值必须是字符串。如果你写成数字4或者布尔值trueClaude Code 会静默忽略不会报错但变量就是没生效。我习惯在 CI 脚本里加一行检查# 别这样写直接依赖 settings.json 里的值# 应该先验证if[-z$WORKER_COUNT];thenechoWORKER_COUNT not set, defaulting to 2exportWORKER_COUNT2fi另一个容易忽略的点env字段的优先级高于系统环境变量但低于 shell 中显式 export 的变量。这意味着如果你在启动 Claude Code 之前已经export OPENAI_API_KEYsk-yyysettings.json 里的值不会覆盖它。这个行为在文档里没写清楚我是通过 strace 跟踪进程环境变量才确认的。shell 继承你以为你继承了其实没有Claude Code 启动时会继承父 shell 的环境变量。但这里有个关键细节继承的是启动时刻的快照不是动态引用。举个例子你在终端里exportMY_VARhelloclaude-code start# 此时 MY_VARhello 被继承exportMY_VARworld# Claude Code 进程里的 MY_VAR 仍然是 hello这个行为在单机开发时问题不大但在容器化部署或 CI/CD 流水线里就麻烦了。比如你在 Dockerfile 里ENV MY_VARhello然后在 entrypoint 脚本里修改了MY_VAR但 Claude Code 启动时拿到的还是 Dockerfile 里的值。我踩过的具体场景在 Kubernetes 的 preStop hook 里修改环境变量期望 Claude Code 的 worker 进程感知到变化。结果发现 worker 进程根本不知道外面变了因为环境变量是进程启动时拷贝的不是共享内存。解决方案用文件或信号机制来传递动态配置别指望环境变量能实时更新。# 别这样写依赖环境变量动态变化# 应该用文件监听# 在 Claude Code 的 settings.json 里配置{env:{CONFIG_FILE:/etc/claude/config.json}}# 然后在代码里监听文件变化平台差异Windows 的坑比想象的多跨平台部署时环境变量的行为差异是最大的坑。我整理了几个典型问题大小写敏感Windows 的环境变量名不区分大小写Linux 区分。你在 settings.json 里写api_key和API_KEY在 Windows 上会被视为同一个变量在 Linux 上是两个。这导致我在 Windows 开发机上测试通过的配置部署到 Linux 服务器上就找不到变量。路径分隔符Windows 用分号;Linux 用冒号:。如果你在env里配置PATH扩展必须根据平台写不同的值。我见过最离谱的 bugWindows 上PATH被错误地加了个冒号导致所有命令都找不到。变量展开Windows 的 cmd 和 PowerShell 对%VAR%和$VAR的处理不同。Claude Code 在 Windows 上启动时如果 settings.json 里写了HOME: %USERPROFILE%这个字符串不会被展开而是原样传递。正确的做法是用绝对路径。换行符Windows 的\r\n和 Linux 的\n会导致多行环境变量值被截断。如果你在 settings.json 里写了一个多行的 SSH 私钥Windows 上可能只读取了第一行。我的经验是在 settings.json 里只放平台无关的变量平台相关的变量通过 shell 包装脚本注入。# Windows 包装脚本 (start.bat)setCLAUDE_HOMEC:\claude claude-code start# Linux 包装脚本 (start.sh)exportCLAUDE_HOME/opt/claudeexecclaude-code start实战一个跨平台的环境变量管理方案经过多次踩坑我总结了一套相对稳定的方案核心思路是“分层管理、显式传递”。第一层系统环境变量。只放最基础的路径和标识比如CLAUDE_INSTALL_DIR、CLAUDE_DATA_DIR。这些在安装时设置之后基本不变。第二层settings.json 的 env 字段。放业务相关的配置比如 API key、日志级别、worker 数量。但注意不要放敏感信息。settings.json 可能被提交到版本控制或者被其他开发者看到。敏感信息应该用 secrets 管理工具。第三层shell 包装脚本。处理平台差异和动态配置。每个平台维护一个启动脚本在脚本里设置平台特定的环境变量然后调用 Claude Code。第四层运行时配置。通过文件或环境变量文件.env加载。Claude Code 支持--env-file参数可以指定一个 .env 文件。这个文件不会被 settings.json 覆盖适合存放动态生成的配置。具体实现# 启动脚本的核心逻辑# 1. 加载平台特定的环境变量source/etc/claude/platform.sh# 2. 加载 secrets从 vault 或 AWS Secrets ManagerexportAPI_KEY$(vaultreadsecret/claude/api_key)# 3. 生成 .env 文件包含动态配置cat/tmp/claude-runtime.envEOF WORKER_ID$(uuidgen)POD_NAME$HOSTNAMEEOF# 4. 启动 Claude Code显式指定 env 文件和 settingsclaude-code start\--settings/etc/claude/settings.json\--env-file /tmp/claude-runtime.env这样分层之后settings.json 只负责静态的、平台无关的配置平台差异和动态内容由脚本处理敏感信息由 secrets 管理。每个层次职责清晰排查问题时也能快速定位。个人经验性建议别在 settings.json 里写死路径。路径是平台差异的重灾区。用环境变量或相对路径然后在启动脚本里解析。每次修改 env 配置后重启 Claude Code 进程。不要相信“热加载”环境变量大部分实现都是启动时读取一次。在 CI 里加一个环境变量检查步骤。写个脚本遍历 settings.json 里引用的所有环境变量确认它们都存在。这个脚本帮我抓到了至少十次配置遗漏。Windows 开发Linux 部署的场景一定要在 Linux 上做集成测试。Windows 上跑得再顺到 Linux 上也可能因为大小写、路径分隔符、换行符等问题翻车。我吃过这个亏现在 CI 里强制跑 Linux 容器。敏感信息永远不要进 settings.json。哪怕你的仓库是私有的settings.json 也可能被不小心分享、备份、或者被 CI 日志打印出来。用 secrets 管理工具或者在启动时从环境变量读取。记录环境变量的来源。当环境变量冲突时知道哪个来源的优先级更高能省去大量排查时间。我习惯在日志里打印每个关键环境变量的来源settings.json / shell 继承 / .env 文件。环境变量管理看起来是小事但在工程化实践中它往往是跨环境部署的第一道坎。把这道坎迈过去后面的路会顺畅很多。