在 Go 中执行长时间运行的外部命令时,可通过 io.MultiWriter 同时将标准输出实时写入终端和内存缓冲区,实现边执行边显示、执行完毕后立即获取完整输出的效果。
在 go 中执行长时间运行的外部命令时,可通过 `io.multiwriter` 同时将标准输出实时写入终端和内存缓冲区,实现边执行边显示、执行完毕后立即获取完整输出的效果。
在开发 CLI 工具或自动化脚本时,常需执行耗时的外部命令(如 git clone、make build 或大型数据处理程序),并希望用户能实时看到输出日志,同时在命令结束后还能对完整输出进行解析、记录或校验。此时若使用 cmd.Output(),会阻塞直到命令结束才返回结果,无法满足“边执行边显示”的需求;而仅设置 cmd.Stdout = os.Stdout 则无法捕获输出内容。
正确做法是利用 Go 标准库中的 io.MultiWriter —— 它可将写入操作同时分发到多个 io.Writer,从而兼顾实时性与可追溯性。以下为完整示例:
package main
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("sh", "-c", "echo 'Starting...'; sleep 1; echo 'Done!'")
var buf bytes.Buffer
// 同时写入终端(os.Stdout)和内存缓冲区(&buf)
cmd.Stdout = io.MultiWriter(os.Stdout, &buf)
cmd.Stderr = io.MultiWriter(os.Stderr, &buf) // 建议同样处理 stderr,避免错误信息丢失
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "Command failed: %v\n", err)
os.Exit(1)
}
// 命令结束后,buf 中已完整保存所有 stdout + stderr 输出
output := buf.String()
fmt.Printf("\n--- Full captured output (%d bytes) ---\n%s", len(output), output)
}
✅ 关键要点:
- io.MultiWriter 是零拷贝、线程安全的组合写入器,性能开销极小;
- bytes.Buffer 实现了 io.Writer 接口,适合临时收集文本输出;
- 务必同时处理 Stderr(如上例所示),否则错误流将被忽略,导致调试困难;
- 若需处理大体积输出(如 GB 级日志),应考虑流式解析或限制缓冲区大小,避免内存溢出;
- buf.Bytes() 返回底层字节切片(可读但不建议直接修改),buf.String() 更适合日志处理。
通过这一模式,你既能提供良好的用户体验(实时反馈),又能保留完整的输出用于后续逻辑(如正则匹配、结果校验、审计存档),是 Go 进程间 I/O 控制的经典实践。











