用os/exec.Command执行命令需注意四点:捕获输出用Output()或管道,错误判断要区分exec.Error和*exec.ExitError,shell特性需显式调用/bin/sh或cmd.exe,超时控制必须用context并合理管理进程组。

直接用 os/exec.Command 启动命令,但多数人卡在输出捕获、错误判断和 shell 特性兼容上。
执行简单命令并获取退出状态
用 cmd.Run() 最简洁,适合只关心成功与否的场景(比如启动服务、触发脚本):
cmd := exec.Command("ls", "-l", "/tmp")
err := cmd.Run()
if err != nil {
// 注意:err 是 *exec.ExitError 类型时,说明进程已运行但非零退出
if exitErr, ok := err.(*exec.ExitError); ok {
fmt.Println("exit code:", exitErr.ExitCode())
}
}
-
Run()会阻塞直到命令结束,自动等待子进程,不返回 stdout/stderr - 参数必须拆成独立字符串(
"ls", "-l", "/tmp"),不能传"ls -l /tmp"这种完整字符串 - 如果命令根本找不到(如拼错命令名),
err是exec.Error,不是ExitError
捕获标准输出和标准错误
要用 cmd.Output() 或手动设置 StdoutPipe/StderrPipe:
cmd := exec.Command("echo", "hello world")
out, err := cmd.Output() // 自动调用 Run() 并返回 stdout 内容
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok && len(exitErr.Stderr) > 0 {
fmt.Println("stderr:", string(exitErr.Stderr)) // 注意:stderr 只在 Output() 中附带在 ExitError 里
}
}
fmt.Println("stdout:", string(out))
-
Output()默认把stderr重定向到stdout,所以错误信息混在返回值里;想分离,得手动配置管道 - 手动配管道时,必须在
Start()前调用StdoutPipe(),否则 panic -
cmd.CombinedOutput()是Output()的变体,明确把stderr合并进返回值,适合日志类调试
需要 shell 功能(管道、重定向、通配符)怎么办
os/exec 默认不经过 shell,所以 exec.Command("ls *.go") 会报 “no such file or directory” —— * 没被展开。
JTBC CMS(5.0) 是一款基于PHP和MySQL的内容管理系统原生全栈开发框架,开源协议为AGPLv3,没有任何附加条款。系统可以通过命令行一键安装,源码方面不基于任何第三方框架,不使用任何脚手架,仅依赖一些常见的第三方类库如图表组件等,您只需要了解最基本的前端知识就能很敏捷的进行二次开发,同时我们对于常见的前端功能做了Web Component方式的封装,即便是您仅了解HTML/CSS也
立即学习“go语言免费学习笔记(深入)”;
- Linux/macOS 下可显式调用
/bin/sh:exec.Command("/bin/sh", "-c", "ls *.go | head -n1") - Windows 下用
cmd.exe:exec.Command("cmd.exe", "/C", "dir *.go") - 注意:
-c和/C后的命令字符串是整体传入,参数不再拆分;变量插值需自己处理(避免注入!) - 如果只是文件通配,更安全的做法是用
filepath.Glob先在 Go 里展开,再传给命令
超时控制和进程生命周期管理
忘记设超时是线上常见故障源 —— 子进程卡住会导致 goroutine 泄漏:
cmd := exec.Command("sleep", "10")
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} // 可选:为后续 kill 整个进程组准备
cmd.Context = ctx
err := cmd.Run()
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
fmt.Println("command timed out")
}
}
-
cmd.Wait()不带超时,别单独用;务必配合context - 若命令启了子进程(如 shell 调起的管道链),单靠
cmd.Process.Kill()可能杀不干净;设Setpgid: true后可用syscall.Kill(-pid, syscall.SIGKILL)杀进程组 -
cmd.ProcessState.Exited()仅在Wait()或Run()返回后才可靠;别在Start()后立刻查
真正麻烦的从来不是“怎么跑命令”,而是“命令没按预期退出时,你有没有拿到足够信息去诊断”。输出捕获方式、错误类型分支、shell 介入时机、超时与信号处理——这四点漏掉任一,都可能让本地测试通过的代码在线上静默失败。









