最可靠方式是直接将*os.file赋值给cmd.stdout,须在cmd.run()前完成,内核自动重定向输出至文件,无缓冲丢失风险。

用 cmd.Stdout 直接赋值文件句柄最可靠
不需要管道、不用 goroutine、不手动 io.Copy,Go 的 exec.Cmd 本身就支持把 stdout 直接指向文件。只要在 cmd.Start() 或 cmd.Run() 之前,把打开的 *os.File 赋给 cmd.Stdout,内核就会把命令输出直接写进那个文件。
常见错误是:先调用 cmd.Run() 再赋值,或者赋值后没检查 os.OpenFile 的错误(比如路径不存在、权限不够)。
-
os.Create("out.txt")会清空已有内容;追加要用os.OpenFile("out.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) - 必须在
cmd.Run()前完成赋值,之后改无效 - 文件写入由操作系统底层完成,
cmd.Wait()返回时内容已落盘(无缓冲丢失风险)
想同时看到终端输出 + 写入文件?用 io.MultiWriter
如果命令执行时你既要实时看到输出(比如调试 ping 或 curl),又得把完整结果存下来,io.MultiWriter 是唯一干净解法。它把一次写操作广播到多个 io.Writer,比如同时发给 os.Stdout 和 *os.File。
别用 cmd.StdoutPipe() 配合 goroutine 去读再写——容易漏 flush、竞态、或命令退出后管道未关闭导致卡住。
-
cmd.Stdout = io.MultiWriter(os.Stdout, outfile)即可,无需额外逻辑 -
bytes.Buffer也能当 writer 用,适合后续做字符串解析 - 若还要捕获 stderr,同理设
cmd.Stderr = io.MultiWriter(os.Stderr, errFile)
fmt.Fprintf 不适用于命令输出重定向
fmt.Fprintf 是用来格式化 Go 变量(如 int、[]string)并写入任意 io.Writer 的,但它**不接管外部进程的 stdout**。有人误以为“把 os.Stdout 换成文件句柄就能让所有 exec.Command 输出进文件”,这是错的——exec.Cmd 启动的是独立进程,它不走 Go 的 fmt 函数链。
混淆点常出现在日志场景:用 log.SetOutput(outfile) 确实能让 log.Printf 进文件,但这和命令输出无关。
-
fmt.Fprintf适合写 Go 自己生成的内容(如结构体、计算结果) - 命令输出必须通过
exec.Cmd.Stdout或StdoutPipe控制 - 强行替换
os.Stdout文件描述符(如 syscall.Dup2)属于底层 hack,绝大多数情况没必要,且不可移植
文件没写入?重点查这三处
90% 的“空文件”问题都出在这三个地方,而不是代码逻辑本身:
- 忘记检查
os.OpenFile返回的err—— 路径含非法字符、父目录不存在、权限被拒都会静默失败 - 用
os.Create但目标路径带多级目录(如"logs/app/output.txt"),而os.Create不自动建父目录,需提前os.MkdirAll("logs/app", 0755) - 赋值
cmd.Stdout后又调了cmd.CombinedOutput()或cmd.Output()—— 这两个方法会覆盖你设的Stdout,改走内存缓冲,导致文件为空
真正难处理的不是写入动作本身,而是路径、权限、生命周期这些看似外围却决定成败的细节。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











