go执行外部命令必须拆分参数,如exec.command("ls", "-l", "/tmp")而非拼接字符串;需用start()+wait()实现超时与实时日志,output()只捕获stdout且不返回stderr,combinedoutput()合并两者但无法区分来源。

Go 里执行外部命令,别直接用 exec.Command + Run 就完事——它不支持超时、无法实时读日志、出错时 stderr 还可能被吞掉。
exec.Command 参数拆分必须严格,不能拼字符串
命令行里写 ls -la /tmp,Go 里得拆成 exec.Command("ls", "-la", "/tmp"),而不是 exec.Command("ls -la /tmp")。后者会尝试找一个叫 "ls -la /tmp" 的可执行文件,必然失败。
- 参数中含空格或特殊字符(如
file name.txt)时,exec.Command自动处理,你只需传"file name.txt"这个字符串,不用手动加引号 - 用户输入若参与构造命令,必须做白名单校验或转义,否则易触发命令注入;例如不要用
exec.Command("sh", "-c", "echo "+ userInput) - Windows 下部分命令(如
dir)是 shell 内置命令,需走exec.Command("cmd", "/c", "dir"),不能直接exec.Command("dir")
Output() 和 CombinedOutput() 的行为差异很关键
Output() 只返回 stdout,遇到非零退出码就报错,且 不会返回 stderr 内容;CombinedOutput() 合并 stdout + stderr 到一个字节切片,但依然无法区分哪行来自哪里。
- 调试阶段建议临时设
cmd.Stderr = os.Stderr,立刻看到真实错误输出 - 想分开捕获:分别给
cmd.Stdout和cmd.Stderr赋值不同的bytes.Buffer或自定义io.Writer - 二者都调用内部的
Run(),所以同样阻塞、无超时、不可中断
需要超时或实时日志?必须用 Start() + Wait() 拆开
用 Run() 或 Output() 就等于放弃控制权。真正可控的方式是 Start() 启动后,再用 Wait() 等待——中间可以杀进程、设 context 超时、读管道流。
- 忘记调
Wait()会导致子进程变成僵尸进程,尤其在循环中反复Start()时风险极高 - 推荐组合:
cmd.Start()→go func() { cmd.Wait() }()+context.WithTimeout监听 Done - 要实时读输出:先调
cmd.StdoutPipe()获取io.ReadCloser,再在 goroutine 里io.Copy(os.Stdout, pipe)
LookPath 不等于“能执行”,生产环境慎用
exec.LookPath("curl") 只查 PATH 下是否存在文件,不检查权限、不验证动态库依赖、更不管容器里有没有装 curl。
- Alpine 镜像里没装 curl,
LookPath成功但Run()报exec: "curl": executable file not found in $PATH - 交叉编译时,Linux 二进制在 macOS 上
LookPath可能成功,但实际执行失败(平台不匹配) - 更可靠的探测方式:
exec.Command("sh", "-c", "command -v curl >/dev/null 2>&1"),前提是系统有sh
最易被忽略的一点:所有管道(StdoutPipe、StderrPipe)必须在 Start() 之前调用,之后调用会 panic;而且一旦设置了 Stdout 字段,StdoutPipe() 就失效了——这两个机制互斥,不能混用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











