添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

原文链接: https://xiets.blog.csdn.net/article/details/130866872

版权声明:原创文章禁止转载

专栏目录: Golang 专栏(总目录)

Go 内置的 os/exec 包用于运行外部命令。它包装了 os.StartProcess 以便更轻松地重新映射 标准输入 和 标准输出、将 I/O 与 管道 连接以及进行其他调整。

exec 包的常用函数:

// 在 PATH 环境变量 路径中搜索名称为 file 的可执行文件, 返回可执行文件的路径。
// 如果 file 中包含了 路径分隔符(斜杠/), 且 file 存在, 则直接返回 file。
// 没有找到可执行文件, 则返回错误。
func LookPath(file string) (string, error)
// 根据 命令名称 和 参数列表, 创建 Cmd 对象。
// 如果 name 不包含路径分隔符, Command() 会尽可能使用 LookPath() 将 name 解析为完整路径。
// 返回的 Cmd 的 Args 字段由 name 和 arg 的一起构成, 因此 arg 不应包含命令名称本身。
func Command(name string, arg ...string) *Cmd
// 与 Command() 类似, 但包含一个上下文。
func CommandContext(ctx context.Context, name string, arg ...string) *Cmd

exec.Cmd 类型和方法:

// Cmd 表示对一个运行命令的封装。
// 在典型的使用中, 一般不直接创建 Cmd 实例, 
// 而是通多调用 exec.Command() 或 exec.CommandContext() 方法创建 Cmd 实例。
type Cmd struct {
	// 要运行命令路径, 这是唯一必须设置为非零值的字段 (其他字段都可以默认为零值)。
	// 如果是相对路径, 则相对于 Dir 字段指定的路径。
	Path string
	// 命令的参数, 包括作为命令本身的 Args[0]。如果 Args 字段为空或 nil, 则 Run() 时使用 {Path}。
	// 在典型的使用中, Path 和 Args 都应该通过调用 exec.Command() 方法来设置。
	Args []string
	// 指定子进程的环境变量, 每一个元素的格式为 "key=value", 如果 Env 为 nil, 则子进程使用当前进程的环境变量。
	// 如果 Env 包含重复的环境变量 key, 则使用切片中的最后一个。
	// 作为 Windows 上的一种特殊情况, 如果缺少 SYSTEMROOT 并且未明确设置为空字符串, 则始终添加 SYSTEMROOT。
	Env []string
	// 命令的工作目录, 如果为零值, 则默认为当前调用 Run() 方法进程的工作目录。
	Dir string
	// 指定子进程的 标准输入。
	// 如果 Stdin 为 nil, 则子进程从 空设备(os.DevNull) 读取。
	// 如果 Stdin 是 *os.File, 则子进程的标准输如直接连接到该文件。
	// 也可以在命令启动前, 调用 cmd.StdinPipe() 方法生成一对读写管道 (返回 Writer), 
	// 需在一个单独的 goroutine 中使用 Writer,
	// Stdin 将被连接到 Reader, 然后往此 Writer 写的数据自动连接到管道的 Reader。
	Stdin io.Reader
	// 指定子进程的 标准输出 和 标准错误 (这两个可以用同一个 Writer)。
	// 如果其中一个为 nil, 则 Run() 方法运行时将其连接到 空设备(os.DevNull)。
	// 如果其中一个是 *os.File, 则子进程的相应输出直接连接到该文件。
	// 也可以在命令启动前, 调用 cmd.StdoutPipe() 和 cmd.StderrPipe() 方法生成一对读写管道 (返回 Reader),
	// 需在一个单独的 goroutine 中使用 Reader,
	// Stdout 和 Stderr 将被连接到 Writer, 然后子进程的输出将从 Reader 中读取。
	Stdout io.Writer
	Stderr io.Writer
	// 指定新进程要继承的其他打开文件。它不包括标准输入、标准输出或标准错误。
	// 如果非零, 条目 i 称为文件描述符 3+i。Windows 不知此此字段。
	ExtraFiles []*os.File
	// 包含可选的、操作系统特定的属性。
	// Run() 方法运行时将它作为 os.ProcAttr 的 Sys 字段传递给 os.StartProcess。
	SysProcAttr *syscall.SysProcAttr
	// 命令运行时的底层进程, 命令启动后可用。
	// 如强制杀进程: Process.Kill()
	Process *os.Process
	// 包含有关已退出进程的信息, 在调用 Wait() 或 Run() 方法后可用。
	// 如获取进程退出状态码: ProcessState.ExitCode()
	ProcessState *os.ProcessState
// 组合输出, 这是一个便捷的操作: 运行命令, 并读取 标准输出 和 标准错误 返回。
func (c *Cmd) CombinedOutput() ([]byte, error)
// 读取标准输出, 这是一个便捷的操作: 运行命令, 并读取 标准输出 返回。
func (c *Cmd) Output() ([]byte, error)
// Start 启动指定的命令,但不等待它完成。如果 Start 返回成功,将设置 c.Process 字段。
func (c *Cmd) Start() error
// 等待命令完成 (子进程退出) 并等待任何复制到 stdin 或从 stdout 或 stderr 复制完成。
// 必须先掉一共 Start() 方法启动命令后才能调用 Wait() 方法。
// 一旦命令退出, Wait() 方法将返回并释放相关资源。
func (c *Cmd) Wait() error
// 启动命令并等待它完成, Start() + Wait() 的组合操作。
func (c *Cmd) Run() error
// 生成一对读写管道 (返回 Writer), 需在一个单独的 goroutine 中使用 Reader,
// c.Stdin 将被连接到 Reader, 然后往此 Writer 写的数据自动连接到管道的 Reader。
// 要使用 StdinPipe 管道, c.Stdin 不能提前设置值。
func (c *Cmd) StdinPipe() (io.WriteCloser, error)
// 生成一对读写管道 (返回 Reader), 需在一个单独的 goroutine 中使用 Writer,
// Stdout/Stderr 将被连接到 Writer, 然后子进程的输出将从 Reader 中读取。
// 要使用 StdoutPipe/StderrPipe 管道, c.Stdout/c.Stderr 不能提前设置值。
func (c *Cmd) StdoutPipe() (io.ReadCloser, error)
func (c *Cmd) StderrPipe() (io.ReadCloser, error)
// 返回 命令+参数 的字符串表示
func (c *Cmd) String() string

exec 运行外部命令,代码示例:

package main
import (
	"bytes"
	"fmt"
	"os/exec"
	"strings"
func main() {
	RunDemo1()
	RunDemo2()
	RunDemo3()
	RunDemo4()
	RunDemo5()
func RunDemo1() {
	fmt.Println("\nRunDemo1()")
	// 创建命令
	cmd := exec.Command("go", "version")
	fmt.Println(cmd.Path)     // /usr/local/go/bin/go
	fmt.Println(cmd.Args)     // [go version]
	fmt.Println(cmd.String()) // /usr/local/go/bin/go version
	// 运行命令, 并获取 标准输出 返回
	out, _ := cmd.Output()
	fmt.Println(string(out)) // go version go1.18.2 darwin/arm64
	// 进程状态
	fmt.Println(cmd.ProcessState.ExitCode()) // 0
	fmt.Println(cmd.ProcessState.Pid())      // 12345
	fmt.Println(cmd.ProcessState.String())   // exit status 0
func RunDemo2() {
	fmt.Println("\nRunDemo2()")
	// 创建命令
	cmd := exec.Command("go", "version")
	// 创建一个 读写缓冲区, 把 cmd.Stdout 连接到缓冲区
	out := bytes.NewBuffer(nil)
	cmd.Stdout = out
	// 启动命令
	_ = cmd.Start()
	// 等待命令完成
	_ = cmd.Wait()
	// 从缓冲区中获取标准输出
	fmt.Println(out.String()) // go version go1.18.2 darwin/arm64
func RunDemo3() {
	fmt.Println("\nRunDemo3()")
	// 创建命令, 把 输入(从Stdin读取)的小写字母 转换为 大写字母输出(写到Stdout)
	cmd := exec.Command("tr", "a-z", "A-Z")
	// 创建一个 Reader, Stdin 连接到 Reader
	in := strings.NewReader("hello world")
	cmd.Stdin = in
	// 创建一个 Writer, Stdout 连接到 Writer
	out := bytes.NewBuffer(nil)
	cmd.Stdout = out
	// 启动命令, 并等待完成
	_ = cmd.Run()
	// 从缓冲区中获取标准输出
	fmt.Println(out.String()) // HELLO WORLD
func RunDemo4() {
	fmt.Println("\nRunDemo4()")
	// 创建命令, 输出当前路径 (MacOS/Linux)
	cmd := exec.Command("pwd")
	// 把子进程的工作目录设置为当前用户目录
	dir, _ := os.UserHomeDir()
	cmd.Dir = dir
	// 运行命令, 并获取 标准输出 和 标准错误
	out, _ := cmd.CombinedOutput()
	fmt.Printf("%s\n", out)
func RunDemo5() {
	fmt.Println("\nRunDemo5()")
	// 创建命令, 输出 所有环境变量 (MacOS/Linux)
	cmd := exec.Command("env")
	// 设置环境变量, 如果直接设置不会把当前进程的环境变量传给进程,
	// 也就是在子进程读取的环境变量只有这里设置的。
	// cmd.Env = []string{"hello=world", "aa=bb"}
	// 可以先读取出当前进程的环境变量, 然后再添加新的环境变量后再设置给 cmd.Env
	env := os.Environ()
	env = append(env, "hello=world")
	env = append(env, "aa=bb")
	cmd.Env = env
	// 获取用于读取 标准输出 的管道 Reader
	pr, _ := cmd.StdoutPipe()
	ch := make(chan bool)
	// 启动命令前, 先启动一个 goroutine, 等待读取标准输出
	go func() {
		// 从管道读取 标准输出
		out, _ := io.ReadAll(pr)
		fmt.Printf("ENV:\n%s\n", out)
		ch <- true
	}()
	// 启动命令, 并等待完成
	_ = cmd.Run()
	// 等待 goroutine 完成
                                    golang调用外部命令,并且通过stdin传数据的例子使用场景:当我们需要调用一个外部命令,然后给外部命令传参数,常用方便的做法是通过命令行传参数,但是有些时候数据太长,或者基于安全考虑,比如传密码,等不方便使用参数时,我们可以通过stdin传递。下面一个docker login传递密码的例子:func dockerLogin(registry string, username string, ...
                                    1. 概述
golang 下的 os/exec 包执行外部命令包执行外部命令。它包装了 os.StartProcess 函数以便更容易的修正输入和输出,使用管道连接I/O,以及作其它的一些调整。
与 C 语言或者其他语言中的“系统”库调用不同, os/exec 包并不调用系统 shell ,也不展开任何 glob (正则匹配)模式,也不处理通常由 shell 完成的其他扩展、管道或重定向。
2. 相关函数
2.1 Variables
var ErrNotFound = errors.New("executa
                                    一般情况下,在 golang 中执行一些命令如 git clone,则可以使用 exec.Command 函数
func RunCommand(path, name string, arg ...string) (msg string, err error) {
	cmd := exec.Command(name, arg...)
	cmd.Dir = path
	err = cmd.Run()
	log.Println(cmd.Args)
	if err != nil {
		log.Println("e
                                    go 是管理 Go 代码的工具,但不仅仅用于编译 Go 代码。下载并安装指定的包及其依赖。go get 干了三件事:一是更新 go.mod 文件将指定包及其版本加入其中;二是下载包源码至模块缓存();三是安装包,生成二进制程序至GOPATH/bin(从 Go 1.17 版本开始,为了更加符合其语义,该功能被废弃,改用go install)。如果包中不包含可执行文件,则不会执行第三步,即 go get 操作成功后 GOPATH/bin 目录下不会有任何编译好的二进制文件。
func Exec(argv0 string, argv []string, envv []string) (err error)
Exec invokes the execve(2) system call.
此方法会将在当前进程空间里,用新的程序覆盖掉当前程序,并执行新的程序,它们依然在同一个进程里,只是进程的内容发生了变化。
main11.go
package main
import (
	"fmt"
	"syscall"
func mai
                                    本文介绍了使用 os/exec 这个标准库调用外部命令的各种姿势。同时为了便于使用, 我编写了一个 goexec 包封装对 os/exec 的调用。这个包目前 for 我自己使用是没有问题的, 大家有其他需求可以提 issue 或者自己魔改😄。大家如果发现好玩、好用的 Go 语言库, 欢迎到 Go 每日一库 GitHub 上提交 issue😄。
                                    程序中执行外部程序是比较常用的功能,Golang执行外部程序可以使用标准库中的 `os/exec` [https://pkg.go.dev/os/exec](https://pkg.go.dev/os/exec) 。这个包默认是用来执行外部程序的,可以通过调用Shell程序来执行Shell命令。这篇文章将对相关使用做个记录,方便自己查阅。
执行单个命令 ls -l ,exec.Command很容易执行,但是需要执行ls -l|wc -l 这样的命令要怎么编写
golang中,需要执行两个命令ls -l  和  wc -l  但是这两个命令是有关系的,只要定义好两个命令的输入输出,就可以完成.
1. 定义两个命令行:
var commands []*exec.Cmd
1、命令源码文件:
声明自己属于 main 代码包、包含无参数声明和结果声明的 main 函数。
命令源码文件被安装以后,GOPATH 如果只有一个工作区,那么相应的可执行文件会被存放当前工作区的 bin 文件夹下;如果有多个工作区,就会安装到 GOBIN 指向的目录下。
命令源码文件是 Go 程序的入口。