Go语言exec包的具体使用
作者:石牌桥网管
在Go中执行外部命令、调用系统程序,核心就是使用标准库os/exec,它能启动进程、传递参数、读写输入输出、控制超时,是脚本化、自动化、调用第三方工具的必备能力,下面就来详细的介绍一下
在 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. 获取命令输出(Output)
package main
import (
"fmt"
"os/exec"
)
func main() {
// Linux/macOS
cmd := exec.Command("echo", "hello go exec")
// Windows: cmd := exec.Command("cmd", "/c", "echo hello go exec")
output, err := cmd.Output()
if err != nil {
fmt.Printf("执行失败: %v\n", err)
return
}
fmt.Printf("输出结果: %s\n", output)
}
Output()会在命令非0退出码时返回错误- 返回的
output是[]byte,直接转字符串即可
2. 获取 stdout + stderr(CombinedOutput)
当需要把日志和错误一起收集时使用:
output, err := cmd.CombinedOutput()
三、只运行不获取输出(Run)
只关心命令是否成功,不关心输出:
cmd := exec.Command("sleep", "1")
err := cmd.Run()
if err != nil {
fmt.Println("执行失败:", err)
}
四、实时输出(不等待缓冲区满)
以前遇到过坑:命令输出很长时,Output() 会阻塞。
正确做法是用管道实时读取:
package main
import (
"io"
"os"
"os/exec"
)
func main() {
// Linux/macOS
cmd := exec.Command("ping", "192.168.0.1", "-c", "3")
// Windows: exec.Command("ping", "192.168.0.1", "-n", "3")
stdout, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}
cmd.Stderr = os.Stderr // 错误直接输出
if err := cmd.Start(); err != nil {
panic(err)
}
// 实时读取输出
io.Copy(os.Stdout, stdout)
// 等待命令结束
if err := cmd.Wait(); err != nil {
panic(err)
}
}
Start():异步启动Wait():等待子进程退出io.Copy:实时流输出
五、给命令传输入(Stdin)
向子进程输入内容:
package main
import (
"os/exec"
"strings"
)
func main() {
// 将字符串作为标准输入传给 cat
cmd := exec.Command("cat")
cmd.Stdin = strings.NewReader("我是输入内容\nhello exec")
output, err := cmd.CombinedOutput()
if err != nil {
panic(err)
}
println(string(output))
}
六、设置工作目录、环境变量
cmd := exec.Command("ls")
cmd.Dir = "/tmp" // 工作目录
cmd.Env = []string{ // 环境变量
"PATH=/usr/bin:/bin",
"MY_ENV=test",
}
七、带超时的安全调用
防止命令卡死:
package main
import (
"context"
"os/exec"
"time"
)
func main() {
// 5秒超时
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 传入ctx
cmd := exec.CommandContext(ctx, "sleep", "10")
err := cmd.Run()
if err != nil {
// 超时会返回 ctx 错误
println("命令结束:", err)
}
}
八、获取退出码
Go 没有直接返回 ExitCode(),需要类型断言:
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
fmt.Println("退出码:", exitErr.ExitCode())
}
}
九、坑点
不要手动拼接命令字符串
错误:
exec.Command("echo hello world") // 错
正确:
exec.Command("echo", "hello", "world")
Windows 要调用 cmd /c
exec.Command("cmd", "/c", "dir")
输出乱码
Windows 下命令多为 GBK,需要转码。
package main
import (
"bytes"
"io"
"os/exec"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
func main() {
// Windows 执行 ipconfig
cmd := exec.Command("ipconfig")
var buf bytes.Buffer
// 输出:GBK → UTF-8
cmd.Stdout = transform.NewWriter(&buf, simplifiedchinese.GBK.NewDecoder())
cmd.Stderr = transform.NewWriter(&buf, simplifiedchinese.GBK.NewDecoder())
_ = cmd.Run()
println(buf.String()) // 中文不乱码
}
Output() 会阻塞
大量输出必须用StdoutPipe实时消费。不处理 Stdout/Stderr 可能导致死锁
输出缓冲区满会阻塞子进程,程序卡住。
package main
import (
"os/exec"
)
// 这个程序会 100% 卡死!
func main() {
// 生成大量输出的命令
// 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)
}
到此这篇关于Go语言exec包的具体使用的文章就介绍到这了,更多相关Go语言 exec包内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
