
本文介绍如何使用 Go 的 exec.Command 同时将子进程的标准输出实时显示在终端,并完整保存为内存中的字符串,避免分段读取,适用于日志记录、文本分析等场景。
本文介绍如何使用 go 的 `exec.command` 同时将子进程的标准输出实时显示在终端,并完整保存为内存中的字符串,避免分段读取,适用于日志记录、文本分析等场景。
在 Go 中调用外部命令时,常需兼顾“实时可见性”与“结果可编程处理”:既要像调试时一样看到命令输出流,又希望最终能以 string 或 []byte 形式完整获取全部 stdout 内容。直接赋值 cmd.Stdout = os.Stdout 会丢失数据引用;而手动循环读取(如 bufio.Scanner)虽可行,却违背“一次性获取全部输出”的简洁需求。
正确解法是利用 io.MultiWriter —— 它允许将同一份输出流同时写入多个 io.Writer,例如既写入 os.Stdout(实现终端回显),又写入 bytes.Buffer(用于后续提取):
package main
import (
"bytes"
"io"
"log"
"os"
"os/exec"
)
func main() {
cmd := exec.Command("ping", "-c", "3", "google.com") // 示例命令,Linux/macOS;Windows 可用 ping -n 3
var out bytes.Buffer
multi := io.MultiWriter(os.Stdout, &out) // 关键:双写目标
cmd.Stdout = multi
if err := cmd.Run(); err != nil {
log.Fatalln("命令执行失败:", err)
}
// 此时 out 已完整缓存全部 stdout
fullOutput := out.String()
log.Printf("✅ 命令执行完毕,共捕获 %d 字符输出", len(fullOutput))
log.Printf("? 全量输出内容:\n%s", fullOutput)
}
⚠️ 注意事项:
- io.MultiWriter 是线程安全的,但仅保证写入顺序一致,不提供并发同步语义;若多 goroutine 同时调用 cmd.Start()/Run(),仍需自行加锁。
- 若命令产生大量输出(GB 级),bytes.Buffer 可能导致内存激增,此时应改用临时文件或流式处理。
- cmd.Stderr 默认未重定向,如需同时捕获错误输出,可同样使用 MultiWriter 绑定 &errBuf,或统一设置 cmd.Stderr = cmd.Stdout(合并流)。
- Windows 下部分命令(如 dir)可能使用 Unicode 编码,out.String() 默认按 UTF-8 解析,若遇乱码,需结合 golang.org/x/text/encoding 显式解码。
总结:io.MultiWriter 是 Go 标准库中轻量、高效且符合 UNIX “单一职责”哲学的解决方案——它不侵入命令逻辑,不增加复杂度,仅通过组合 Writer 接口,便优雅实现“所见即所得”的双向输出需求。











