Go语言执行系统命令包 os/exec 用例详解

os/exec包提供了执行外部命令的方法,它包装了os.StartProcess函数以便更容易的修正输入和输出,使用管道连接I/O。

Cmd struct

type Cmd struct {
    
    
    // Path是将要执行的命令的路径。
    //
    // 该字段不能为空,如为相对路径会相对于Dir字段。
    Path string
    // Args保管命令的参数,包括命令名作为第一个参数;如果为空切片或者nil,相当于无参数命令。
    //
    // 典型用法下,Path和Args都应被Command函数设定。
    Args []string
    // Env指定进程的环境,如为nil,则是在当前进程的环境下执行。
    Env []string
    // Dir指定命令的工作目录。如为空字符串,会在调用者的进程当前目录下执行。
    Dir string
    // Stdin指定进程的标准输入,如为nil,进程会从空设备读取(os.DevNull)
    Stdin io.Reader
    // Stdout和Stderr指定进程的标准输出和标准错误输出。
    //
    // 如果任一个为nil,Run方法会将对应的文件描述符关联到空设备(os.DevNull)
    //
    // 如果两个字段相同,同一时间最多有一个线程可以写入。
    Stdout io.Writer
    Stderr io.Writer
    // ExtraFiles指定额外被新进程继承的已打开文件流,不包括标准输入、标准输出、标准错误输出。
    // 如果本字段非nil,entry i会变成文件描述符3+i。
    //
    // BUG: 在OS X 10.6系统中,子进程可能会继承不期望的文件描述符。
    // http://golang.org/issue/2603
    ExtraFiles []*os.File
    // SysProcAttr保管可选的、各操作系统特定的sys执行属性。
    // Run方法会将它作为os.ProcAttr的Sys字段传递给os.StartProcess函数。
    SysProcAttr *syscall.SysProcAttr
    // Process是底层的,只执行一次的进程。
    Process *os.Process
    // ProcessState包含一个已经存在的进程的信息,只有在调用Wait或Run后才可用。
    ProcessState *os.ProcessState
    // 内含隐藏或非导出字段
}

Cmd代表一个正在准备或者在执行中的外部命令。

cmd 接口函数

func Command(name string, arg ...string) *Cmd
函数返回一个*Cmd,用于使用给出的参数执行name指定的程序。返回值只设定了Path和Args两个参数。
如果name不含路径分隔符,将使用LookPath获取完整路径;否则直接使用name。参数arg不应包含命令名。

func (c *Cmd) StdinPipe() (io.WriteCloser, error)
StdinPipe方法返回一个在命令Start后与命令标准输入关联的管道。Wait方法获知命令结束后会关闭这个管道。必要时调用者可以调用Close方法来强行关闭管道,例如命令在输入关闭后才会执行返回时需要显式关闭管道。

func (c *Cmd) StdoutPipe() (io.ReadCloser, error)
StdoutPipe方法返回一个在命令Start后与命令标准输出关联的管道。Wait方法获知命令结束后会关闭这个管道,一般不需要显式的关闭该管道。但是在从管道读取完全部数据之前调用Wait是错误的;同样使用StdoutPipe方法时调用Run函数也是错误的。参见下例:

func (c *Cmd) StderrPipe() (io.ReadCloser, error)
StderrPipe方法返回一个在命令Start后与命令标准错误输出关联的管道。Wait方法获知命令结束后会关闭这个管道,一般不需要显式的关闭该管道。但是在从管道读取完全部数据之前调用Wait是错误的;同样使用StderrPipe方法时调用Run函数也是错误的。请参照StdoutPipe的例子。

func (c *Cmd) Run() error
Run执行c包含的命令,并阻塞直到完成。

如果命令成功执行,stdinstdoutstderr的转交没有问题,并且返回状态码为0,方法的返回值为nil;如果命令没有执行或者执行失败,会返回*ExitError类型的错误;否则返回的error可能是表示I/O问题。

func (c *Cmd) Start() error
Start开始执行c包含的命令,但并不会等待该命令完成即返回。Wait方法会返回命令的返回状态码并在命令返回后释放相关的资源。

func (c *Cmd) Wait() error
Wait会阻塞直到该命令执行完成,该命令必须是被Start方法开始执行的。

如果命令成功执行,stdinstdoutstderr的转交没有问题,并且返回状态码为0,方法的返回值为nil;如果命令没有执行或者执行失败,会返回*ExitError类型的错误;否则返回的error可能是表示I/O问题。Wait方法会在命令返回后释放相关的资源。

func (c *Cmd) Output() ([]byte, error)
执行命令并返回标准输出的切片。

func (c *Cmd) CombinedOutput() ([]byte, error)
执行命令并返回标准输出和错误输出合并的切片。

实例 1

package main

import (
	"bytes"
	"encoding/json"
	"log"
	"os/exec"
	"strings"
)

func main() {
    
    
	//设置日志
	log.SetFlags(log.Ldate | log.Llongfile)
	//执行外部命令
	cmd := exec.Command("ifconfig", "-a")

	var out bytes.Buffer
	cmd.Stdout = &out	//标准输出内容到 out中
	
	err := cmd.Start()
	if err != nil {
    
    
		log.Fatal("err")
	}
	log.Printf("waiting for command to finish...")
	err = cmd.Wait()	//等待 cmd 执行完成
	log.Printf("command finished with error: %v", err)
	log.Println("command result:", out.String())

	var str string = out.String()
	pos := strings.Index(str, "mtu")
	log.Println(pos)

}

运行结果

Starting: /home/robot/golang/bin/dlv-dap dap --listen=127.0.0.1:42993 --log-dest=3
DAP server listening at: 127.0.0.1:42993
2021/08/17 /home/robot/go-workspace/src/project/exec/exec.go:51: waiting for command to finish...
2021/08/17 /home/robot/go-workspace/src/project/exec/exec.go:53: command finished with error: <nil>
2021/08/17 /home/robot/go-workspace/src/project/exec/exec.go:54: command result: ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.15.160  netmask 255.255.255.0  broadcast 192.168.15.255
        inet6 fe80::c8dd:6c50:25ef:4ea7  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:05:c2:d4  txqueuelen 1000  (Ethernet)
        RX packets 120123  bytes 105783331 (105.7 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 45580  bytes 5040674 (5.0 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 6035  bytes 784187 (784.1 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 6035  bytes 784187 (784.1 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


2021/08/17 /home/robot/go-workspace/src/project/exec/exec.go:58: 51
Process 30571 has exited with status 0
Detaching
dlv dap (30492) exited with code: 0
		

实例 2

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os/exec"
	"strings"
)

func ExampleCmd_StdoutPipe() {
    
    
	cmd := exec.Command("echo", "-n", `{
    
    "Name": "Bob", "Age": 32}`)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
    
    
		log.Fatal(err)
	}
	if err := cmd.Start(); err != nil {
    
    
		log.Fatal(err)
	}
	var person struct {
    
    
		Name string
		Age  int
	}
	if err := json.NewDecoder(stdout).Decode(&person); err != nil {
    
    
		log.Fatal(err)
	}
	if err := cmd.Wait(); err != nil {
    
    
		log.Fatal(err)
	}
	fmt.Printf("%s is %d years old\n", person.Name, person.Age)
}

func ExampleCmd_StdinPipe() {
    
    
	cmd := exec.Command("cat")
	stdin, err := cmd.StdinPipe()
	if err != nil {
    
    
		log.Fatal(err)
	}

	go func() {
    
    
		defer stdin.Close()
		io.WriteString(stdin, "values written to stdin are passed to cmd's standard input")
	}()

	out, err := cmd.CombinedOutput()
	if err != nil {
    
    
		log.Fatal(err)
	}

	fmt.Printf("%s\n", out)
}

func main() {
    
    
	ExampleCmd_StdoutPipe()
	ExampleCmd_StdinPipe()

}

运行结果

Starting: /home/robot/golang/bin/dlv-dap dap --listen=127.0.0.1:36115 --log-dest=3
DAP server listening at: 127.0.0.1:36115
Bob is 32 years old
values written to stdin are passed to cmd's standard input
Process 32053 has exited with status 0
Detaching
dlv dap (31896) exited with code: 0

猜你喜欢

转载自blog.csdn.net/weixin_38387929/article/details/119763963