Go语言exec包的具体使用

 更新时间:2026年06月01日 15:03:14   作者:石牌桥网管  
在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 + stderr
  • cmd.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()) // 中文不乱码
}
  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包内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Golang Web 框架Iris安装部署

    Golang Web 框架Iris安装部署

    这篇文章主要为大家介绍了Golang Web 框架Iris安装部署,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Go语言的缓存策略与实现方法代码示例

    Go语言的缓存策略与实现方法代码示例

    在Go语言中缓存是一种常见的优化技术,用于减少重复计算或I/O操作,提高程序性能,这篇文章主要介绍了Go语言缓存策略与实现方法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2026-04-04
  • Golang中下划线(_)的不错用法分享

    Golang中下划线(_)的不错用法分享

    golang中的下划线表示忽略变量的意思,也没有产生新的变量,但是后面的表达式依然会被执行,本文为大家整理了golang中下划线的一些不错的用法,需要的可以参考下
    2023-05-05
  • Golang如何交叉编译各个平台的二进制文件详解

    Golang如何交叉编译各个平台的二进制文件详解

    这篇文章主要给大家介绍了关于Golang如何交叉编译各个平台的二进制文件的相关资料,并介绍了golang如何让编译生产的二进制文件变小,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-08-08
  • go语言接口之接口值举例详解

    go语言接口之接口值举例详解

    接口是一种抽象类型,是对其他类型行为的概括与抽象,从语法角度来看,接口是一组方法定义的集合,下面这篇文章主要给大家介绍了关于go语言接口之接口值的相关资料,文章通过代码介绍的非常详细,需要的朋友可以参考下
    2024-06-06
  • 从源码解析golang Timer定时器体系

    从源码解析golang Timer定时器体系

    本文详细介绍了Go语言中的Timer和Ticker的使用方式、错误使用方式以及底层源码实现,Timer是一次性的定时器,而Ticker是循环定时器,正确使用时需要注意返回的channel和垃圾回收问题,Go 1.23版本对定时器进行了改进,优化了垃圾回收和停止、重置相关方法
    2025-01-01
  • golang下的viper包的简单使用方式

    golang下的viper包的简单使用方式

    这篇文章主要介绍了golang下的viper包的简单使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • golang中interface接口的深度解析

    golang中interface接口的深度解析

    什么是interface,简单的说,interface是一组method的组合,下面这篇文章主要给大家深度解析了关于golang中的interface接口,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-11-11
  • GO语言基础库os包的函数全面解析

    GO语言基础库os包的函数全面解析

    这篇文章主要为大家介绍了GO语言基础库os包的函数全面解析, 有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • 基于go-cqhttp与Flask搭建定制机器人项目实战示例

    基于go-cqhttp与Flask搭建定制机器人项目实战示例

    这篇文章主要为大家介绍了基于go-cqhttp与Flask搭建定制机器人项目实战示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11

最新评论