exec.Command 传参需分离命令与参数,如 exec.Command("ls", "-la");Run()仅返回错误,Output()自动检查退出码,CombinedOutput()捕获完整输出;须显式设置Dir和Env;务必用CommandContext设超时。

exec.Command 传参时别把命令和参数拼成一个字符串
直接用 exec.Command("ls -la") 会报错:「exec: "ls -la": executable file not found in $PATH」。这是因为 exec.Command 第一个参数是程序名,后续每个参数必须单独传,shell 的空格分隔逻辑不会自动解析。
正确写法是:exec.Command("ls", "-la", "/tmp")。如果参数本身含空格(比如文件路径),也得作为独立字符串传入,不要手动加引号——Go 会原样传递给子进程,由系统处理。
- 错误:
exec.Command("curl -X POST -d 'name=foo' http://api.com") - 正确:
exec.Command("curl", "-X", "POST", "-d", "name=foo", "http://api.com") - 带空格路径:用
exec.Command("cp", "/path/with space/file.txt", "/dest/"),不是"'/path/with space/file.txt'"
cmd.Run() 和 cmd.Output() 的行为差异直接影响错误捕获
cmd.Run() 只返回 error,不保存输出;cmd.Output() 返回 []byte 和 error,但会自动检查 exit code —— 非零就直接返回 error,哪怕 stdout 有内容。这点容易误判失败原因。
比如 git commit 没有暂存区变更时退出码为 1,但 cmd.Output() 会直接报错,你拿不到 stderr 里的提示信息「nothing to commit」。
立即学习“go语言免费学习笔记(深入)”;
- 要捕获完整输出(含 stderr)并自己判断 exit code:用
cmd.CombinedOutput() - 只关心是否成功、不关心输出:用
cmd.Run() - 需要 stdout 内容且允许非零退出码:用
cmd.StdoutPipe()+cmd.Start()+cmd.Wait()手动控制
环境变量和工作目录不继承父进程,得显式设置
Go 默认不复制 os.Environ(),也不使用当前工作目录(os.Getwd()),子进程的 $PWD 是根目录,$PATH 可能极简。常见现象是:命令存在但找不到,或配置文件读取失败。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
必须主动设置:
- 工作目录:
cmd.Dir = "/my/project" - 环境变量:
cmd.Env = append(os.Environ(), "HOME=/tmp", "LANG=en_US.UTF-8")(注意:要基于os.Environ()追加,不能覆盖) - 想清空所有环境再设最小集?可以,但需明确列出
PATH,否则多数命令直接失败
阻塞 vs 超时:cmd.Wait() 默认无限等待,生产环境必须设 timeout
没加超时的 cmd.Run() 或 cmd.Wait() 可能让整个 goroutine 卡死,比如被调用的 ping 或 ssh 因网络问题 hang 住。Go 标准库不提供内置 timeout,得靠 context。
正确做法是用 exec.CommandContext:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() cmd := exec.CommandContext(ctx, "rsync", "...")
一旦超时,context 会取消,cmd.Wait() 返回 context.DeadlineExceeded 错误。注意:某些命令(如 sleep)收到 SIGKILL 后可能残留僵尸进程,Linux 下一般自动回收,但高频率调用时建议加 cmd.Process.Kill() 做兜底。
真正麻烦的是交互式命令(如 gpg --passphrase-fd)或需要信号转发的场景——这时候别硬扛,换 os/exec 配合 syscall.SysProcAttr 控制子进程组更稳妥。

















