Go语言执行cmd命令库的方法实现
作者:242030
有时候我们需要通过代码的方式去执行 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
到此这篇关于Go语言执行cmd命令库的方法实现的文章就介绍到这了,更多相关Go执行cmd内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!