os/exec.command 是 go 调用外部命令最稳妥方式,需拆分参数、显式配置标准流、手动加超时控制;错误写法如 exec.command("ls -l /tmp") 会报文件未找到,正确为 exec.command("ls", "-l", "/tmp")。

用 os/exec.Command 启动子进程最稳妥
直接调用系统命令或外部二进制时,os/exec.Command 是 Go 标准库唯一推荐的方式。它不走 sh -c,避免 shell 注入;默认不继承父进程环境,安全性可控。
常见错误是传参写成一整条字符串,比如 exec.Command("ls -l /tmp") —— 这会报 exec: "ls -l /tmp": executable file not found in $PATH。正确做法是拆成独立参数:
cmd := exec.Command("ls", "-l", "/tmp")
如果确实需要 shell 功能(如管道、通配符),显式调用 sh 并传入 -c:
cmd := exec.Command("sh", "-c", "ls /tmp/*.log | head -n 1")
子进程 stdin/stdout/stderr 通信要手动配置
默认情况下,子进程的三个标准流都连接到当前进程的对应流(即终端)。想做双向通信,必须在 Start() 前显式设置:
-
cmd.Stdin = strings.NewReader("hello\n"):向子进程写入输入 -
stdout, _ := cmd.StdoutPipe():获取可读管道,配合io.ReadAll(stdout)读输出 -
cmd.Stderr = os.Stderr或重定向到文件,避免错误被吞掉
注意:StdoutPipe() 和 StderrPipe() 必须在 Start() 前调用,否则 panic;且不能多次调用同一 Pipe 方法。
阻塞等待与超时控制必须自己加
cmd.Run() 会阻塞直到子进程退出,但没超时机制。生产环境务必加限制:
cmd := exec.Command("sleep", "10")
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
done := make(chan error, 1)
go func() { done <p>别用 <code>cmd.Output()</code> 直接替代——它内部调用 <code>Run()</code>,同样无超时;也别依赖 <code>cmd.ProcessState.Exited()</code> 判断是否结束,它只在 <code>Wait()</code> 后才有效。</p><h3>跨平台路径和权限问题经常被忽略</h3><p>Windows 下启动可执行文件必须带 <code>.exe</code> 后缀(即使 PATH 中有),而 Linux/macOS 不需要;Go 编译的二进制若未加 <code>+x</code> 权限,在 Linux 上会报 <code>permission denied</code>,不是 “file not found”。</p><p>检查路径是否可执行的简单方式:</p><pre class="brush:php;toolbar:false;">if _, err := exec.LookPath("mytool"); err != nil {
log.Fatal("mytool not found in PATH or not executable:", err)
}
另外,子进程无法自动继承父进程的 Go context 取消信号,如需响应取消,得用 cmd.Process.Signal(os.Interrupt) 手动转发。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











