exec.command 默认不继承父进程环境变量,仅带极简默认环境;需用 os.environ() 获取全量环境并显式赋值给 cmd.env,避免遗漏 path 或泄露敏感信息。

exec.Command 默认不继承父进程环境变量
很多人以为 exec.Command 启动的子进程会自动带上当前 Go 程序的所有环境变量,其实不会。它只带一个极简的默认环境(比如 PATH 可能都为空),除非你显式设置。
常见错误现象:exec: "curl": executable file not found in $PATH,或者子进程读不到你预期的 CONFIG_ENV=prod。
- 必须手动传入完整环境:用
cmd.Env赋值,不是追加 - Go 1.19+ 开始,
os.Environ()返回的是当前进程全部环境变量的拷贝,安全可用 - 别直接用
os.Setenv改全局环境——那对子进程无效,只影响当前 Go 进程后续的os.Getenv
正确设置子进程环境变量的写法
核心是两步:先获取父进程环境,再按需增删改,最后赋给 cmd.Env。不能只 append 一两个变量,否则会丢失 PATH 等关键项。
示例:
cmd := exec.Command("sh", "-c", "echo $MY_VAR && echo $PATH")
env := os.Environ() // 拿全量
env = append(env, "MY_VAR=hello") // 增加
env = append(env, "PATH=/usr/local/bin:/usr/bin:/bin") // 覆盖 PATH,谨慎!
cmd.Env = env
- 如果只想覆盖个别变量(如
HOME),建议遍历os.Environ(),跳过原值再 append 新值 - Windows 下注意路径分隔符是
;,但os.Environ()已处理好,无需手动转义 - 避免在
env切片里重复 key:某些系统(如 Linux)以第一个出现为准,有些(如 macOS)可能取最后一个
exec.CommandContext 和环境变量的关系
exec.CommandContext 只负责信号控制和超时,跟环境变量完全无关。它生成的 *exec.Cmd 对象仍需手动设 cmd.Env。
- 别误以为加了 context 就自动继承环境——不会
- 若用
context.WithValue往 context 塞环境变量,子进程也收不到,那是 Go runtime 内部传参机制,不落地到 OS 层 - 超时触发
cmd.Wait()返回context.DeadlineExceeded时,环境变量设置早已生效,不影响子进程已启动的状态
敏感环境变量泄露风险
把 os.Environ() 全量传给子进程,等于把父进程所有环境变量(含 TOKEN、SECRET_KEY、HOME 路径)一股脑交出去,尤其调用第三方二进制时很危险。
- 生产环境务必白名单过滤:只保留
PATH、LANG、TERM等必要项,显式构造cmd.Env - 不要依赖子进程自己从
HOME读配置——万一它顺手把~/.aws/credentials也读了呢 - 调试时可临时打印
cmd.Env内容,但上线前删掉,避免日志泄露
环境变量不是“设了就完事”,它是子进程看到的整个外部世界快照,错一点,要么找不到命令,要么暴露凭证。最稳妥的做法永远是:最小集 + 显式声明。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











