0


Go语言执行cmd命令库

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方法

funcCommand(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")funcmain(){// 创建一个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 05月  19 09:27 aa.txt
-rw-r--r--. 1 root root 05月  19 09:27 bb.txt
-rw-r--r--. 1 root root 05月  19 09:27 cc.txt
package main

import("bytes""fmt""os/exec")funcmain(){// 创建一个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 05月  19 09:27 aa.txt
-rw-r--r--. 1 root root 05月  19 09:27 bb.txt
-rw-r--r--. 1 root root 05月  19 09:27 cc.txt

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

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

的相对路径去查找。

package main

import("fmt""os/exec")funcmain(){
    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")funcmain(){// 进行字符串的替换
    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")funcmain(){
    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 1620:55 aa.txt
-rw-r--r--. 1 root root 0 Jun 1620:56 bb.txt
-rw-r--r--. 1 root root 0 Jun 1620:56 cc.txt
package main

import("fmt""os/exec")funcmain(){  
    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 1620:55 aa.txt
-rw-r--r--. 1 root root 0 Jun 1620:56 bb.txt
-rw-r--r--. 1 root root 0 Jun 1620: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")funcmain(){
    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")funcmain(){
    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将关闭这些pipefunc(c *Cmd)StderrPipe()(io.ReadCloser,error)
// StdinPipe返回一个连接到command标准输入的管道pipefunc(c *Cmd)StdinPipe()(io.WriteCloser,error)
// StdoutPipe返回一个连接到command标准输出的管道pipefunc(c *Cmd)StdoutPipe()(io.ReadCloser,error)
package main

import("fmt""os""os/exec")funcmain(){
    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")funcmain(){
    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 1620:55 aa.txt
-rw-r--r--. 1 root root 0 Jun 1620:56 bb.txt
-rw-r--r--. 1 root root 0 Jun 1620:56 cc.txt

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

package main

import("log""os""os/exec")funcmain(){
    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 1620:55 aa.txt
-rw-r--r--. 1 root root 0 Jun 1620:56 bb.txt
-rw-r--r--. 1 root root 0 Jun 1620: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")funcmain(){
    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 1620:55 aa.txt
-rw-r--r--. 1 root root 0 Jun 1620:56 bb.txt
-rw-r--r--. 1 root root 0 Jun 1620:56 cc.txt
package main

import("encoding/json""fmt""log""os/exec")funcmain(){
    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")funcmain(){
    log.Println("start")CanKillRun()
    log.Println("end")}// 执行完发挥的数据结构type result struct{
    err    error
    output []byte}// 能够杀死的进程funcCanKillRun(){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可以理解是子进程去处理gofunc(){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)funcmain(){
    command :="/bin/bash"
    params :=[]string{"-c","sh test.sh"}execCommand(command, params)}funcexecCommand(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())returnfalse}// 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()returntrue}
$ 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
标签: golang

本文转载自: https://blog.csdn.net/qq_30614345/article/details/131464116
版权归原作者 242030 所有, 如有侵权,请联系我们删除。

“Go语言执行cmd命令库”的评论:

还没有评论