Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Go语言 exec包

Go语言exec包的具体使用

作者:石牌桥网管

在Go中执行外部命令、调用系统程序,核心就是使用标准库os/exec,它能启动进程、传递参数、读写输入输出、控制超时,是脚本化、自动化、调用第三方工具的必备能力,下面就来详细的介绍一下

在 Go 中执行外部命令、调用系统程序,核心就是使用标准库 os/exec。它能启动进程、传递参数、读写输入输出、控制超时,是脚本化、自动化、调用第三方工具的必备能力。

本文实例讲解 exec 包常用用法。

一、exec 包核心概念

os/exec 用于创建和控制子进程,与直接系统调用不同:

常用结构与函数:

二、最简单示例:执行命令并获取输出

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)
}

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)
	}
}

五、给命令传输入(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()) // 中文不乱码
}
  1. Output() 会阻塞
    大量输出必须用 StdoutPipe 实时消费。

  2. 不处理 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包内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文