最简路径是用exec.command创建命令对象后调用cmd.combinedoutput()获取合并输出;若需分离stdout/stderr,则须手动设置cmd.stdout和cmd.stderr为bytes.buffer实例,并始终检查err。

用 os/exec 执行命令并获取 stdout/stderr 最简路径
直接用 exec.Command 创建命令对象,再调用 cmd.CombinedOutput() 是最快拿到全部输出的方式——但它把 stdout 和 stderr 合并了,调试时容易混淆。真要分开捕获,得手动设置 cmd.Stdout 和 cmd.Stderr 为 bytes.Buffer 实例。
常见错误是只设 Stdout 却忘了 Stderr,结果 stderr 还打到终端上;或者用了 cmd.Output() 却没处理非零退出码,导致程序 panic。
-
cmd := exec.Command("ls", "-l")—— 参数必须拆成字符串切片,不能传完整命令行 - 务必检查
err:即使 stdout 有内容,cmd.Run()或cmd.Output()失败时仍会返回 error - 如果命令可能长时间运行,记得设
cmd.WaitDelay(Go 1.20+)或自己加 context 控制超时
带超时和上下文取消的健壮调用方式
生产环境里,不加超时的 os/exec 调用等于埋雷。用 context.WithTimeout 包裹命令执行,既能防卡死,又能响应外部取消信号。
注意:cmd.Start() + cmd.Wait() 组合才真正受 context 控制;cmd.Run() 内部虽用 context,但某些旧版 Go 中对 SIGKILL 响应不及时。
- 别直接用
time.Sleep模拟超时——它无法中断正在运行的子进程 - 设置
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}可避免子进程脱离控制(尤其在 Linux 上 kill 整个进程组时) - 如果命令本身需要交互输入,得显式设置
cmd.Stdin = os.Stdin,否则默认是 nil,读取会立即 EOF
区分 exit code、stdout、stderr 的典型判断逻辑
很多开发者以为 err != nil 就代表命令失败,其实 exec.ExitError 才对应非零退出码;而 exec.Error(比如找不到命令)才是真正的执行失败。
正确做法是先看 err 类型,再 cast 成 *exec.ExitError 拿 .ExitCode(),同时检查 stdout 和 stderr 内容是否为空——有些工具(如 git)成功时也往 stderr 写提示信息。
- 用
errors.Is(err, exec.ErrNotFound)判断命令不存在,比字符串匹配更可靠 -
stderr.Bytes()返回的是原始字节,中文需用string(stderr.Bytes())转换,别直接 fmt.Println 二进制内容 - 若命令输出量大(>1MB),避免用
bytes.Buffer,改用io.Pipe流式处理,防止内存爆掉
Windows 下路径和 shell 行为的坑
在 Windows 上,dir、echo 这类命令不是独立可执行文件,而是 cmd.exe 内置命令。直接 exec.Command("dir") 会报 "executable file not found"。
解决办法是显式调用 cmd /c dir,但要注意:/c 会等命令结束才退出,/k 则不会;而且 Windows 路径分隔符反斜杠在 Go 字符串里要双写 "C:\temp" 或用 raw string `C: emp`。
- 跨平台代码里,别硬编码
"/bin/sh"或"cmd.exe",用runtime.GOOS分支判断 - PowerShell 命令(如
Get-Process)必须走powershell -Command "...",且引号嵌套极易出错,建议拼接时用fmt.Sprintf并转义双引号 - Windows 默认编码是 GBK,
chcp 65001切到 UTF-8 后,Go 读取的 stdout 才能正确解析中文
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











