exec.command 不会自动执行命令,必须显式调用 run、output 或 combinedoutput;exec.command("ls -l") 必报错,因 go 不经 shell 解析,首参数须为程序名,后续为独立参数,正确写法是 exec.command("ls", "-l")。

exec.Command 不会自动执行命令,必须显式调用 Run、Output 或 CombinedOutput —— 写完 exec.Command("ls") 就以为它跑了,程序静默退出,是新手最常踩的坑。
为什么 exec.Command("ls -l") 一定报错
Go 不经过 shell 解析,exec.Command 的第一个参数必须是可执行文件名,后续每个参数都是独立字符串。写成 exec.Command("ls -l"),Go 会去找一个叫 "ls -l" 的二进制,自然失败。
- 正确写法:
exec.Command("ls", "-l")—— 命令和参数拆开传 - 路径含空格也照拆:
exec.Command("cp", "my file.txt", "/tmp/dest"),不用加引号,Go 自动处理 - 需要管道、通配符、变量展开?显式走 shell:
exec.Command("sh", "-c", "ls *.go | head -1") - Windows 下注意后缀:
exec.Command("ping", "google.com")可行,但exec.Command("notepad.exe")在 Linux 会失败
Run、Output、CombinedOutput 怎么选
选错方法会导致拿不到 stderr、OOM 或误判成功。
-
Run():只关心成败,不捕获任何输出。适合mkdir、git pull这类“执行即忘”操作。stdout/stderr 直接打到终端,你代码里读不到 -
Output():自动捕获 stdout 到内存,但 stderr 默认仍输出到终端(除非你重定向)。别用它跑日志量大的命令,比如tail -f或 dump 大文件,容易爆内存 -
CombinedOutput():合并 stdout 和 stderr 到一个[]byte,适合调试或错误必须和输出一起看的场景(比如grep查不存在的文件) - 想分开处理?手动设:
cmd.Stdout = &bytes.Buffer{}+cmd.Stderr = &bytes.Buffer{},再调Run()
为什么命令卡住、超时不管用、或者变成僵尸进程
根本原因是没控制生命周期 —— Run() 和 Output() 是黑盒同步调用,没法中断、没法写 stdin、也没法实时读流。
- 必须超时?用
exec.CommandContext(ctx, ...),其中ctx := context.WithTimeout(context.Background(), 5*time.Second);只靠time.AfterFunc+ 手动Kill()不可靠 - 要写 stdin?先设
cmd.Stdin = strings.NewReader("yes\n"),再调Start();Start()后改Stdin会 panic - 忘记
Wait()?子进程会变成僵尸,尤其在循环中反复Start()时 - 环境变量默认不继承:
cmd.Env = os.Environ()才能拿到$PATH,否则容器里常报"curl": executable file not found
如何安全判断命令是否“业务失败”
Output() 和 Run() 都不检查退出码。比如 curl https://404.example 返回 404,Run() 仍可能返回 nil 错误 —— 因为进程启动成功了,只是 HTTP 语义失败。
- 必须调完
Run()或Wait()后,再查cmd.ProcessState.ExitCode() - 更稳妥:用
errors.As(err, &exec.ExitError{})判断是否为子进程异常退出 - 注意信号终止的情况:
ExitCode()返回-1,得用ProcessState.Signal()检查是否被SIGKILL杀掉 - 别依赖
LookPath做存在性判断:它只查路径,不验权限、不验动态库,Alpine 镜像里ffmpeg找得到但跑不起来很常见
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











