在 Go 中执行外部命令、调用系统程序核心就是使用标准库os/exec。它能启动进程、传递参数、读写输入输出、控制超时是脚本化、自动化、调用第三方工具的必备能力。本文实例讲解exec包常用用法。一、exec 包核心概念os/exec用于创建和控制子进程与直接系统调用不同自带安全处理不会自动注入 Shell更安全支持分离Stdout/Stderr/Stdin可等待、杀死、设置超时、获取退出码多平台通用常用结构与函数exec.Command(name, arg...)创建命令对象cmd.Run()运行并等待完成cmd.Output()运行并返回标准输出cmd.CombinedOutput()返回 stdout stderrcmd.Start()cmd.Wait()异步启动异步等待cmd.StdoutPipe()实时读取输出二、最简单示例执行命令并获取输出1. 获取命令输出Outputpackagemainimport(fmtos/exec)funcmain(){// Linux/macOScmd:exec.Command(echo,hello go exec)// Windows: cmd : exec.Command(cmd, /c, echo hello go exec)output,err:cmd.Output()iferr!nil{fmt.Printf(执行失败: %v\n,err)return}fmt.Printf(输出结果: %s\n,output)}Output()会在命令非0退出码时返回错误返回的output是[]byte直接转字符串即可2. 获取 stdout stderrCombinedOutput当需要把日志和错误一起收集时使用output,err:cmd.CombinedOutput()三、只运行不获取输出Run只关心命令是否成功不关心输出cmd:exec.Command(sleep,1)err:cmd.Run()iferr!nil{fmt.Println(执行失败:,err)}四、实时输出不等待缓冲区满以前遇到过坑命令输出很长时Output() 会阻塞。正确做法是用管道实时读取packagemainimport(ioosos/exec)funcmain(){// Linux/macOScmd:exec.Command(ping,192.168.0.1,-c,3)// Windows: exec.Command(ping, 192.168.0.1, -n, 3)stdout,err:cmd.StdoutPipe()iferr!nil{panic(err)}cmd.Stderros.Stderr// 错误直接输出iferr:cmd.Start();err!nil{panic(err)}// 实时读取输出io.Copy(os.Stdout,stdout)// 等待命令结束iferr:cmd.Wait();err!nil{panic(err)}}Start()异步启动Wait()等待子进程退出io.Copy实时流输出五、给命令传输入Stdin向子进程输入内容packagemainimport(os/execstrings)funcmain(){// 将字符串作为标准输入传给 catcmd:exec.Command(cat)cmd.Stdinstrings.NewReader(我是输入内容\nhello exec)output,err:cmd.CombinedOutput()iferr!nil{panic(err)}println(string(output))}六、设置工作目录、环境变量cmd:exec.Command(ls)cmd.Dir/tmp// 工作目录cmd.Env[]string{// 环境变量PATH/usr/bin:/bin,MY_ENVtest,}七、带超时的安全调用防止命令卡死packagemainimport(contextos/exectime)funcmain(){// 5秒超时ctx,cancel:context.WithTimeout(context.Background(),5*time.Second)defercancel()// 传入ctxcmd:exec.CommandContext(ctx,sleep,10)err:cmd.Run()iferr!nil{// 超时会返回 ctx 错误println(命令结束:,err)}}八、获取退出码Go 没有直接返回ExitCode()需要类型断言iferr!nil{ifexitErr,ok:err.(*exec.ExitError);ok{fmt.Println(退出码:,exitErr.ExitCode())}}九、坑点不要手动拼接命令字符串错误exec.Command(echo hello world)// 错正确exec.Command(echo,hello,world)Windows 要调用 cmd /cexec.Command(cmd,/c,dir)输出乱码Windows 下命令多为 GBK需要转码。packagemainimport(bytesioos/execgolang.org/x/text/encoding/simplifiedchinesegolang.org/x/text/transform)funcmain(){// Windows 执行 ipconfigcmd:exec.Command(ipconfig)varbuf bytes.Buffer// 输出GBK → UTF-8cmd.Stdouttransform.NewWriter(buf,simplifiedchinese.GBK.NewDecoder())cmd.Stderrtransform.NewWriter(buf,simplifiedchinese.GBK.NewDecoder())_cmd.Run()println(buf.String())// 中文不乱码}Output() 会阻塞大量输出必须用StdoutPipe实时消费。不处理 Stdout/Stderr 可能导致死锁输出缓冲区满会阻塞子进程程序卡住。packagemainimport(os/exec)// 这个程序会 100% 卡死funcmain(){// 生成大量输出的命令// Windows: cmd /c echo 大量行for /L %i in (1,1,5000) do echo 这是一行测试数据cmd:exec.Command(cmd,/c,echo 大量行for /L %i in (1,1,5000) do echo 这是一行测试数据)// 关键错误不设置 Stdout、Stderr// 管道缓冲区会被填满子进程阻塞cmd.Run() 永远不返回err:cmd.Run()println(永远到不了这里,err)}