Go語言執(zhí)行cmd命令庫的方法實現(xiàn)
有時候我們需要通過代碼的方式去執(zhí)行 linux 命令,那么 os/exec 這個系統(tǒng)庫剛好提供了相應的功能。
Golang語言中提供了一個 os/exec 包,它提供了一組函數(shù)和結構,用于調(diào)用外部程序,這些外部程序可以是系統(tǒng)
自帶的,也可以是用戶自定義的。os/exec 包中提供了一組函數(shù),用于執(zhí)行系統(tǒng)命令,我們可以使用它來執(zhí)行系
統(tǒng)的cmd命令行。
exec包執(zhí)行外部命令,它將 os.StartProcess 進行包裝使得它更容易映射到 stdin 和 stdout,并且利用 pipe 連接i/o。
參考文檔:https://pkg.go.dev/os/exec
1、Command方法
func Command(name string, arg ...string) *Cmd {}使用 exec.Command 函數(shù)來創(chuàng)建一個 Cmd 結構體,該函數(shù)接受兩個參數(shù),第一個參數(shù)是要執(zhí)行的命令,第二個
參數(shù)是命令行參數(shù),比如 ls -l,那么第一個參數(shù)就是 ls,第二個參數(shù)就是 -l。
2、Run方法
func (c *Cmd) Run() error {}使用 Run 函數(shù)來執(zhí)行這個命令,Run 函數(shù)會根據(jù)我們傳入的參數(shù)來執(zhí)行命令,并返回一個 error 類型的結果。
3、Output方法
可以使用 Output 函數(shù)來獲取命令執(zhí)行的結果。
4、簡單例子
package main
import (
"fmt"
"os/exec"
)
func main() {
// 創(chuàng)建一個Cmd結構體
cmd := exec.Command("ls", "-l", "/opt/software/")
// // 不需要cmd.Run()
out, err := cmd.Output()
if err != nil {
fmt.Println("執(zhí)行命令出錯: ", err)
return
} else {
fmt.Println("獲取命令執(zhí)行結果: ", string(out))
}
}# 程序輸出 獲取命令執(zhí)行結果: 總用量 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() {
// 創(chuàng)建一個Cmd結構體
cmd := exec.Command("ls", "-l", "/opt/software/")
// 設置輸出
var stdout bytes.Buffer
cmd.Stdout = &stdout
// 執(zhí)行命令
err := cmd.Run()
if err != nil {
fmt.Println("執(zhí)行命令出錯: ", err)
return
} else {
fmt.Println("獲取命令執(zhí)行結果: ", stdout.String())
}
}# 程序輸出 獲取命令執(zhí)行結果: 總用量 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命令的可執(zhí)行二進制文件
LookPath 在環(huán)境變量中查找科執(zhí)行二進制文件,如果file中包含一個斜杠,則直接根據(jù)絕對路徑或者相對本目錄
的相對路徑去查找。
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、對標準輸入執(zhí)行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、執(zhí)行命令Run和Start
// 開始指定命令并且等待它執(zhí)行結束,如果命令能夠成功執(zhí)行完畢,則返回nil,否則的話邊會產(chǎn)生錯誤 func (c *Cmd) Run() error
// 使某個命令開始執(zhí)行,但是并不等到他執(zhí)行結束,這點和Run命令有區(qū)別,然后使用Wait方法等待命令執(zhí)行完畢并且釋放響應的資源 func (c *Cmd) Start() error
注:一個 command 只能使用 Start() 或者 Run() 中的一個啟動命令,不能兩個同時使用。
Start 執(zhí)行不會等待命令完成,Run會阻塞等待命令完成。
下面看一下兩個命令的區(qū)別:
package main
import (
"log"
"os/exec"
)
func main() {
log.Println("start")
cmd := exec.Command("sleep", "10")
// 執(zhí)行到此處時會阻塞等待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")
// 執(zhí)行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
// 執(zhí)行命令
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
}# 查看生成的文件的內(nèi)容 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一起使用,如果命令能夠順利執(zhí)行完并順利退出則返回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、執(zhí)行過程中想要殺死cmd的執(zhí)行
package main
import (
"context"
"fmt"
"log"
"os/exec"
"time"
)
func main() {
log.Println("start")
CanKillRun()
log.Println("end")
}
// 執(zhí)行完發(fā)揮的數(shù)據(jù)結構
type result struct {
err error
output []byte
}
// 能夠殺死的進程
func CanKillRun() {
var (
cmd *exec.Cmd
ctx context.Context
cancelFunc context.CancelFunc
resultChan chan *result
res *result
)
// 創(chuàng)建一個通道用戶協(xié)程交換數(shù)據(jù)
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;")
// 執(zhí)行任務,捕捉輸出
output, err = cmd.CombinedOutput()
// 把任務執(zhí)行結果輸出給main協(xié)程
resultChan <- &result{
err: err,
output: output,
}
}()
// 1s后我們就把他殺死
// 繼續(xù)往下走
time.Sleep(1 * time.Second)
// 取消上下文
cancelFunc()
// 讀取通道里面的數(shù)據(jù)
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、執(zhí)行腳本并獲取結果
package main
import (
"bufio"
"fmt"
"io"
"os"
"os/exec"
"strings"
)
/*
test.sh腳本內(nèi)容
#!/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("執(zhí)行命令: %s\n", strings.Join(cmd.Args, " "))
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Fprintln(os.Stderr, "error=>", err.Error())
return false
}
// Start開始執(zhí)行包含的命令,但并不會等待該命令完成即返回
// wait方法會返回命令的返回狀態(tài)碼并在命令返回后釋放相關的資源
cmd.Start()
reader := bufio.NewReader(stdout)
var index int
// 實時循環(huán)讀取輸出流中的一行內(nèi)容
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 執(zhí)行命令: /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語言執(zhí)行cmd命令庫的方法實現(xiàn)的文章就介紹到這了,更多相關Go執(zhí)行cmd內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
GoFrame框架數(shù)據(jù)校驗之校驗結果Error接口對象
這篇文章主要為大家介紹了GoFrame框架數(shù)據(jù)校驗之校驗結果Error接口對象示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06
Golang?rabbitMQ生產(chǎn)者消費者實現(xiàn)示例
這篇文章主要為大家介紹了Golang?rabbitMQ生產(chǎn)者消費者實現(xiàn)的示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪2022-04-04

