Go语言执行cmd命令库

有时候我们需要通过代码的方式去执行 linux 命令,那么 os/exec 这个系统库刚好提供了相应的功能。

Golang语言中提供了一个 os/exec 包,它提供了一组函数和结构,用于调用外部程序,这些外部程序可以是系统

自带的,也可以是用户自定义的。os/exec 包中提供了一组函数,用于执行系统命令,我们可以使用它来执行系

统的cmd命令行。

exec包执行外部命令,它将 os.StartProcess 进行包装使得它更容易映射到 stdin 和 stdout,并且利用 pipe 连接

i/o。

参考文档:https://pkg.go.dev/os/exec

1、Command方法

func Command(name string, arg ...string) *Cmd {}

使用 exec.Command 函数来创建一个 Cmd 结构体,该函数接受两个参数,第一个参数是要执行的命令,第二个

参数是命令行参数,比如 ls -l,那么第一个参数就是 ls,第二个参数就是 -l。

2、Run方法

func (c *Cmd) Run() error {}

使用 Run 函数来执行这个命令,Run 函数会根据我们传入的参数来执行命令,并返回一个 error 类型的结果。

3、Output方法

可以使用 Output 函数来获取命令执行的结果。

4、简单例子

package main

import (

"fmt"

"os/exec"

)

func main() {

// 创建一个Cmd结构体

cmd := exec.Command("ls", "-l", "/opt/software/")

// // 不需要cmd.Run()

out, err := cmd.Output()

if err != nil {

fmt.Println("执行命令出错: ", err)

return

} else {

fmt.Println("获取命令执行结果: ", string(out))

}

}

# 程序输出

获取命令执行结果: 总用量 0

-rw-r--r--. 1 root root 0 5月 19 09:27 aa.txt

-rw-r--r--. 1 root root 0 5月 19 09:27 bb.txt

-rw-r--r--. 1 root root 0 5月 19 09:27 cc.txt

package main

import (

"bytes"

"fmt"

"os/exec"

)

func main() {

// 创建一个Cmd结构体

cmd := exec.Command("ls", "-l", "/opt/software/")

// 设置输出

var stdout bytes.Buffer

cmd.Stdout = &stdout

// 执行命令

err := cmd.Run()

if err != nil {

fmt.Println("执行命令出错: ", err)

return

} else {

fmt.Println("获取命令执行结果: ", stdout.String())

}

}

# 程序输出

获取命令执行结果: 总用量 0

-rw-r--r--. 1 root root 0 5月 19 09:27 aa.txt

-rw-r--r--. 1 root root 0 5月 19 09:27 bb.txt

-rw-r--r--. 1 root root 0 5月 19 09:27 cc.txt

5、查找cmd命令的可执行二进制文件

LookPath 在环境变量中查找科执行二进制文件,如果file中包含一个斜杠,则直接根据绝对路径或者相对本目录

的相对路径去查找。

package main

import (

"fmt"

"os/exec"

)

func main() {

f, err := exec.LookPath("ls")

if err != nil {

fmt.Println(err)

}

// /usr/bin/ls

fmt.Println(f)

}

6、对标准输入执行cmd命令

package main

import (

"bytes"

"fmt"

"os/exec"

"strings"

"log"

)

func main() {

// 进行字符串的替换

cmd := exec.Command("tr", "a-z", "A-Z")

cmd.Stdin = strings.NewReader("some input")

var out bytes.Buffer

cmd.Stdout = &out

err := cmd.Run()

if err != nil {

log.Fatal(err)

}

// in all caps: SOME INPUT

fmt.Printf("in all caps: %s\n", out.String())

}

7、标准输出Output和CombinedOutput

// 运行命令,并返回标准输出和标准错误

func (c *Cmd) CombinedOutput() ([]byte, error)

//运行命令并返回其标准输出

func (c *Cmd) Output() ([]byte, error)

注意:Output() 和 CombinedOutput() 不能够同时使用,因为 command 的标准输出只能有一个,同时使用的话

便会定义了两个,便会报错。

package main

import (

"fmt"

"os/exec"

)

func main() {

cmd := exec.Command("ls", "-l", "/opt/software/")

out, err := cmd.CombinedOutput()

if err != nil {

fmt.Println(err)

}

fmt.Println(string(out))

}

$ go run 005.go

total 0

-rw-r--r--. 1 root root 0 Jun 16 20:55 aa.txt

-rw-r--r--. 1 root root 0 Jun 16 20:56 bb.txt

-rw-r--r--. 1 root root 0 Jun 16 20:56 cc.txt

package main

import (

"fmt"

"os/exec"

)

func main() {

cmd := exec.Command("ls", "-l", "/opt/software/")

out, err := cmd.Output()

if err != nil {

fmt.Println(err)

}

fmt.Println(string(out))

}

$ go run 006.go

total 0

-rw-r--r--. 1 root root 0 Jun 16 20:55 aa.txt

-rw-r--r--. 1 root root 0 Jun 16 20:56 bb.txt

-rw-r--r--. 1 root root 0 Jun 16 20:56 cc.txt

8、执行命令Run和Start

// 开始指定命令并且等待它执行结束,如果命令能够成功执行完毕,则返回nil,否则的话边会产生错误

func (c *Cmd) Run() error

// 使某个命令开始执行,但是并不等到他执行结束,这点和Run命令有区别,然后使用Wait方法等待命令执行完毕并且释放响应的资源        

func (c *Cmd) Start() error          

注:一个 command 只能使用 Start() 或者 Run() 中的一个启动命令,不能两个同时使用。

Start 执行不会等待命令完成,Run会阻塞等待命令完成。

下面看一下两个命令的区别:

package main

import (

"log"

"os/exec"

)

func main() {

log.Println("start")

cmd := exec.Command("sleep", "10")

// 执行到此处时会阻塞等待10秒

err := cmd.Run()

if err != nil {

log.Fatal(err)

}

log.Println("end")

}

$ go run 007-1.go

2023/06/17 08:21:51 start

2023/06/17 08:22:01 end

package main

import (

"log"

"os/exec"

)

func main() {

log.Println("start")

cmd := exec.Command("sleep", "10")

// 如果用start则直接向后运行

err := cmd.Start()

if err != nil {

log.Fatal(err)

}

log.Println("end")

// 执行Start会在此处等待10秒

err = cmd.Wait()

if err != nil {

log.Fatal(err)

}

log.Println("wait")

}

$ go run 007-2.go

2023/06/17 08:23:53 start

2023/06/17 08:23:53 end

2023/06/17 08:24:03 wait

9、管道Pipe

// StderrPipe返回一个pipe,这个管道连接到command的标准错误,当command命令退出时,wait将关闭这些pipe

func (c *Cmd) StderrPipe() (io.ReadCloser, error)

// StdinPipe返回一个连接到command标准输入的管道pipe

func (c *Cmd) StdinPipe() (io.WriteCloser, error)

// StdoutPipe返回一个连接到command标准输出的管道pipe

func (c *Cmd) StdoutPipe() (io.ReadCloser, error)

package main

import (

"fmt"

"os"

"os/exec"

)

func main() {

cmd := exec.Command("cat")

stdin, err := cmd.StdinPipe()

if err != nil {

fmt.Println(err)

}

_, err = stdin.Write([]byte("tmp.txt"))

if err != nil {

fmt.Println(err)

}

stdin.Close()

// 终端标准输出tmp.txt

cmd.Stdout = os.Stdout

}

$ go run 008.go

tmp.txt

package main

import (

"io/ioutil"

"log"

"os/exec"

)

func main() {

cmd := exec.Command("ls", "-l", "/opt/software/")

// 获取输出对象,可以从该对象中读取输出结果

stdout, err := cmd.StdoutPipe()

if err != nil {

log.Fatal(err)

}

// 保证关闭输出流

defer stdout.Close()

// 运行命令

if err := cmd.Start(); err != nil {

log.Fatal(err)

}

// 读取输出结果

if opBytes, err := ioutil.ReadAll(stdout); err != nil {

log.Fatal(err)

} else {

log.Println(string(opBytes))

}

if err := cmd.Wait(); err != nil {

log.Fatal(err)

}

}

$ go run 009.go

total 0

-rw-r--r--. 1 root root 0 Jun 16 20:55 aa.txt

-rw-r--r--. 1 root root 0 Jun 16 20:56 bb.txt

-rw-r--r--. 1 root root 0 Jun 16 20:56 cc.txt

10、将命令的输出结果重定向到文件中

package main

import (

"log"

"os"

"os/exec"

)

func main() {

cmd := exec.Command("ls", "-l", "/opt/software/")

stdout, err := os.OpenFile("stdout.log", os.O_CREATE|os.O_WRONLY, 0600)

if err != nil {

log.Fatalln(err)

}

defer stdout.Close()

// 重定向标准输出到文件

cmd.Stdout = stdout

// 执行命令

if err := cmd.Start(); err != nil {

log.Fatal(err)

}

if err := cmd.Wait(); err != nil {

log.Fatal(err)

}

}

# 查看生成的文件的内容

total 0

-rw-r--r--. 1 root root 0 Jun 16 20:55 aa.txt

-rw-r--r--. 1 root root 0 Jun 16 20:56 bb.txt

-rw-r--r--. 1 root root 0 Jun 16 20:56 cc.txt

11、Wait

// Wait等待command退出,它必须和Start一起使用,如果命令能够顺利执行完并顺利退出则返回nil,否则的话便会返回error,其中Wait会是放掉所有与cmd命令相关的资源

func (c *Cmd) Wait() error

package main

import (

"io/ioutil"

"log"

"os/exec"

)

func main() {

cmd := exec.Command("ls", "-l", "/opt/software/")

// 指向cmd命令的stdout

stdout, err := cmd.StdoutPipe()

if err != nil {

log.Fatal(err)

}

defer stdout.Close()

if err := cmd.Start(); err != nil {

log.Fatal(err)

}

if opBytes, err := ioutil.ReadAll(stdout); err != nil {

log.Fatal(err)

} else {

log.Println(string(opBytes))

}

if err := cmd.Wait(); err != nil {

log.Fatal(err)

}

}

$ go run 011.go

total 0

-rw-r--r--. 1 root root 0 Jun 16 20:55 aa.txt

-rw-r--r--. 1 root root 0 Jun 16 20:56 bb.txt

-rw-r--r--. 1 root root 0 Jun 16 20:56 cc.txt

package main

import (

"encoding/json"

"fmt"

"log"

"os/exec"

)

func main() {

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)

}

// Bob is 32 years old

fmt.Printf("%s is %d years old\n", person.Name, person.Age)

}

12、执行过程中想要杀死cmd的执行

package main

import (

"context"

"fmt"

"log"

"os/exec"

"time"

)

func main() {

log.Println("start")

CanKillRun()

log.Println("end")

}

// 执行完发挥的数据结构

type result struct {

err error

output []byte

}

// 能够杀死的进程

func CanKillRun() {

var (

cmd *exec.Cmd

ctx context.Context

cancelFunc context.CancelFunc

resultChan chan *result

res *result

)

// 创建一个通道用户协程交换数据

resultChan = make(chan *result, 1000)

// 拿到这个上下文的取消方法

ctx, cancelFunc = context.WithCancel(context.TODO())

// 起一个goroutine可以理解是子进程去处理

go func() {

var (

output []byte

err error

)

cmd = exec.CommandContext(ctx, "bash", "-c", "sleep 3;echo hello;")

// 执行任务,捕捉输出

output, err = cmd.CombinedOutput()

// 把任务执行结果输出给main协程

resultChan <- &result{

err: err,

output: output,

}

}()

// 1s后我们就把他杀死

// 继续往下走

time.Sleep(1 * time.Second)

// 取消上下文

cancelFunc()

// 读取通道里面的数据

res = <-resultChan

// 打印结果

fmt.Println("err: ", res.err, " out: ", string(res.output))

}

$ go run 013.go

2023/06/17 08:36:52 start

err: signal: killed out:

2023/06/17 08:36:55 end

13、执行脚本并获取结果

package main

import (

"bufio"

"fmt"

"io"

"os"

"os/exec"

"strings"

)

/*

test.sh脚本内容

#!/bin/bash

for k in $( seq 1 10 )

do

echo "Hello World $k"

sleep 1

done

*/

var contentArray = make([]string, 0, 5)

func main() {

command := "/bin/bash"

params := []string{"-c", "sh test.sh"}

execCommand(command, params)

}

func execCommand(commandName string, params []string) bool {

contentArray = contentArray[0:0]

cmd := exec.Command(commandName, params...)

// 显示运行的命令

fmt.Printf("执行命令: %s\n", strings.Join(cmd.Args, " "))

stdout, err := cmd.StdoutPipe()

if err != nil {

fmt.Fprintln(os.Stderr, "error=>", err.Error())

return false

}

// Start开始执行包含的命令,但并不会等待该命令完成即返回

// wait方法会返回命令的返回状态码并在命令返回后释放相关的资源

cmd.Start()

reader := bufio.NewReader(stdout)

var index int

// 实时循环读取输出流中的一行内容

for {

line, err2 := reader.ReadString('\n')

if err2 != nil || io.EOF == err2 {

break

}

fmt.Println(line)

index++

contentArray = append(contentArray, line)

}

cmd.Wait()

return true

}

$ go run 014.go

执行命令: /bin/bash -c sh test.sh

Hello World 1

Hello World 2

Hello World 3

Hello World 4

Hello World 5

Hello World 6

Hello World 7

Hello World 8

Hello World 9

Hello World 10

精彩链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: