
本文介绍如何使用 go 的 `os/exec` 包安全、高效地调用系统 shell(如 bash、zsh),并实现标准输入 / 输出 / 错误的双向管道通信,支持交互式会话与批处理命令执行。
在 Go 中与系统命令行 Shell 交互,核心在于正确管理进程生命周期和 I/O 管道。Go 标准库 的 os/exec 包提供了强大而底层的接口,但需手动处理 stdin/stdout/stderr 的读写同步,否则易出现阻塞、死锁或输出丢失等问题。
以下是一个完整、可运行的示例程序,它能:
- 启动交互式 Shell(如 bash),支持用户实时输入与命令响应;
- 执行一次性命令(如 date、ping -c 3 google.com),并原样输出结果;
- 使用 goroutine + sync.WaitGroup 实现多路 I/O 并发转发,避免管道阻塞。
package main import ("bufio" "fmt" "io" "log" "os" "os/exec" "sync") // getPipes 为命令创建标准流管道,失败时直接 panic(演示场景下简化错误处理)func getPipes(c *exec.Cmd) (inp io.Writer, outp, errp io.Reader) {var err error if inp, err = c.StdinPipe(); err != nil {log.Fatal("stdin pipe:", err) } if outp, err = c.StdoutPipe(); err != nil { log.Fatal("stdout pipe:", err) } if errp, err = c.StderrPipe(); err != nil { log.Fatal("stderr pipe:", err) } return } // pipe 将输入流(如 stdout)逐字节复制到输出流(如 os.Stdout),支持并发转发 func pipe(wg *sync.WaitGroup, inp io.Reader, outp io.Writer) {if wg != nil { defer wg.Done() } r := bufio.NewReader(inp) for {b, err := r.ReadByte() if err != nil {break // EOF 或其他错误,退出循环} _, _ = fmt.Fprint(outp, string(b)) } } // Command 执行指定命令,自动处理参数拆分与 I/O 管道绑定 func Command(args ……string) {if len(args) == 0 {log.Fatal("no command specified") } cmd := exec.Command(args[0], args[1:]……) stdin, stdout, stderr := getPipes(cmd) var wg sync.WaitGroup wg.Add(2) go pipe(&wg, stdout, os.Stdout) go pipe(&wg, stderr, os.Stderr) go pipe(nil, os.Stdin, stdin) // 不等待 stdin 转发完成(用户可能持续输入)if err := cmd.Start(); err != nil { log.Fatalf("failed to start %v: %v", args, err) } // 等待命令退出(对交互式 shell 来说,即用户输入 exit 或 Ctrl+D)if err := cmd.Wait(); err != nil { log.Printf("command %v exited with error: %v", args, err) } wg.Wait() // 确保所有输出已刷新完毕} func main() { // 示例:启动交互式 bash 会话(退出后继续执行后续命令)Command("bash") // 示例:执行一次性命令 Command("date") Command("echo", "some text") Command("ping", "-c", "3", "www.google.com") }
✅ 关键要点说明:
- exec.Command() 创建命令对象,不立即执行;必须调用 Start() 启动进程,Wait() 等待结束;
- StdinPipe() / StdoutPipe() 返回的 io.ReadWriter 需由 goroutine 并发处理,否则因缓冲区满导致阻塞(尤其 ping 或长输出命令);
- 对于交互式 Shell(如 bash),cmd.Wait() 会阻塞直到 shell 进程退出(用户输入 exit 或关闭终端),此时程序才继续执行下一条 Command;
- 生产环境建议替换 log.Fatal 为可恢复的错误处理,并添加上下文(context.Context)支持超时与取消;
- 安全性提醒:避免将不可信输入直接拼入 args 数组(防止命令注入),尤其调用 /bin/sh -c 时;优先使用显式参数列表而非 shell 解析。
该方案跨平台兼容(Linux/macOS/Windows),是构建轻量系统 工具、CLI 自动化脚本或嵌入式 Shell 控制台的理想起点。






























