Go語(yǔ)言執(zhí)行cmd命令庫(kù)的方法實(shí)現(xiàn)
有時(shí)候我們需要通過代碼的方式去執(zhí)行 linux 命令,那么 os/exec
這個(gè)系統(tǒng)庫(kù)剛好提供了相應(yīng)的功能。
Golang語(yǔ)言中提供了一個(gè) os/exec
包,它提供了一組函數(shù)和結(jié)構(gòu),用于調(diào)用外部程序,這些外部程序可以是系統(tǒng)
自帶的,也可以是用戶自定義的。os/exec
包中提供了一組函數(shù),用于執(zhí)行系統(tǒng)命令,我們可以使用它來(lái)執(zhí)行系
統(tǒng)的cmd命令行。
exec包執(zhí)行外部命令,它將 os.StartProcess 進(jìn)行包裝使得它更容易映射到 stdin 和 stdout,并且利用 pipe 連接i/o。
參考文檔:https://pkg.go.dev/os/exec
1、Command方法
func Command(name string, arg ...string) *Cmd {}
使用 exec.Command 函數(shù)來(lái)創(chuàng)建一個(gè) Cmd 結(jié)構(gòu)體,該函數(shù)接受兩個(gè)參數(shù),第一個(gè)參數(shù)是要執(zhí)行的命令,第二個(gè)
參數(shù)是命令行參數(shù),比如 ls -l,那么第一個(gè)參數(shù)就是 ls,第二個(gè)參數(shù)就是 -l。
2、Run方法
func (c *Cmd) Run() error {}
使用 Run 函數(shù)來(lái)執(zhí)行這個(gè)命令,Run 函數(shù)會(huì)根據(jù)我們傳入的參數(shù)來(lái)執(zhí)行命令,并返回一個(gè) error 類型的結(jié)果。
3、Output方法
可以使用 Output 函數(shù)來(lái)獲取命令執(zhí)行的結(jié)果。
4、簡(jiǎn)單例子
package main import ( "fmt" "os/exec" ) func main() { // 創(chuàng)建一個(gè)Cmd結(jié)構(gòu)體 cmd := exec.Command("ls", "-l", "/opt/software/") // // 不需要cmd.Run() out, err := cmd.Output() if err != nil { fmt.Println("執(zhí)行命令出錯(cuò): ", err) return } else { fmt.Println("獲取命令執(zhí)行結(jié)果: ", string(out)) } }
# 程序輸出 獲取命令執(zhí)行結(jié)果: 總用量 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)建一個(gè)Cmd結(jié)構(gòu)體 cmd := exec.Command("ls", "-l", "/opt/software/") // 設(shè)置輸出 var stdout bytes.Buffer cmd.Stdout = &stdout // 執(zhí)行命令 err := cmd.Run() if err != nil { fmt.Println("執(zhí)行命令出錯(cuò): ", err) return } else { fmt.Println("獲取命令執(zhí)行結(jié)果: ", stdout.String()) } }
# 程序輸出 獲取命令執(zhí)行結(jié)果: 總用量 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í)行二進(jìn)制文件
LookPath 在環(huán)境變量中查找科執(zhí)行二進(jìn)制文件,如果file中包含一個(gè)斜杠,則直接根據(jù)絕對(duì)路徑或者相對(duì)本目錄
的相對(duì)路徑去查找。
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、對(duì)標(biāo)準(zhǔn)輸入執(zhí)行cmd命令
package main import ( "bytes" "fmt" "os/exec" "strings" "log" ) func main() { // 進(jìn)行字符串的替換 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、標(biāo)準(zhǔn)輸出Output和CombinedOutput
// 運(yùn)行命令,并返回標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤 func (c *Cmd) CombinedOutput() ([]byte, error)
//運(yùn)行命令并返回其標(biāo)準(zhǔn)輸出 func (c *Cmd) Output() ([]byte, error)
注意:Output() 和 CombinedOutput() 不能夠同時(shí)使用,因?yàn)?command 的標(biāo)準(zhǔn)輸出只能有一個(gè),同時(shí)使用的話
便會(huì)定義了兩個(gè),便會(huì)報(bào)錯(cuò)。
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í)行結(jié)束,如果命令能夠成功執(zhí)行完畢,則返回nil,否則的話邊會(huì)產(chǎn)生錯(cuò)誤 func (c *Cmd) Run() error
// 使某個(gè)命令開始執(zhí)行,但是并不等到他執(zhí)行結(jié)束,這點(diǎn)和Run命令有區(qū)別,然后使用Wait方法等待命令執(zhí)行完畢并且釋放響應(yīng)的資源 func (c *Cmd) Start() error
注:一個(gè) command 只能使用 Start() 或者 Run() 中的一個(gè)啟動(dòng)命令,不能兩個(gè)同時(shí)使用。
Start 執(zhí)行不會(huì)等待命令完成,Run會(huì)阻塞等待命令完成。
下面看一下兩個(gè)命令的區(qū)別:
package main import ( "log" "os/exec" ) func main() { log.Println("start") cmd := exec.Command("sleep", "10") // 執(zhí)行到此處時(shí)會(huì)阻塞等待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則直接向后運(yùn)行 err := cmd.Start() if err != nil { log.Fatal(err) } log.Println("end") // 執(zhí)行Start會(huì)在此處等待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返回一個(gè)pipe,這個(gè)管道連接到command的標(biāo)準(zhǔn)錯(cuò)誤,當(dāng)command命令退出時(shí),wait將關(guān)閉這些pipe func (c *Cmd) StderrPipe() (io.ReadCloser, error)
// StdinPipe返回一個(gè)連接到command標(biāo)準(zhǔn)輸入的管道pipe func (c *Cmd) StdinPipe() (io.WriteCloser, error)
// StdoutPipe返回一個(gè)連接到command標(biāo)準(zhǔn)輸出的管道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() // 終端標(biāo)準(zhǔn)輸出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/") // 獲取輸出對(duì)象,可以從該對(duì)象中讀取輸出結(jié)果 stdout, err := cmd.StdoutPipe() if err != nil { log.Fatal(err) } // 保證關(guān)閉輸出流 defer stdout.Close() // 運(yùn)行命令 if err := cmd.Start(); err != nil { log.Fatal(err) } // 讀取輸出結(jié)果 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、將命令的輸出結(jié)果重定向到文件中
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() // 重定向標(biāo)準(zhǔn)輸出到文件 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,否則的話便會(huì)返回error,其中Wait會(huì)是放掉所有與cmd命令相關(guān)的資源 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í)行過程中想要?dú)⑺纁md的執(zhí)行
package main import ( "context" "fmt" "log" "os/exec" "time" ) func main() { log.Println("start") CanKillRun() log.Println("end") } // 執(zhí)行完發(fā)揮的數(shù)據(jù)結(jié)構(gòu) type result struct { err error output []byte } // 能夠殺死的進(jìn)程 func CanKillRun() { var ( cmd *exec.Cmd ctx context.Context cancelFunc context.CancelFunc resultChan chan *result res *result ) // 創(chuàng)建一個(gè)通道用戶協(xié)程交換數(shù)據(jù) resultChan = make(chan *result, 1000) // 拿到這個(gè)上下文的取消方法 ctx, cancelFunc = context.WithCancel(context.TODO()) // 起一個(gè)goroutine可以理解是子進(jìn)程去處理 go func() { var ( output []byte err error ) cmd = exec.CommandContext(ctx, "bash", "-c", "sleep 3;echo hello;") // 執(zhí)行任務(wù),捕捉輸出 output, err = cmd.CombinedOutput() // 把任務(wù)執(zhí)行結(jié)果輸出給main協(xié)程 resultChan <- &result{ err: err, output: output, } }() // 1s后我們就把他殺死 // 繼續(xù)往下走 time.Sleep(1 * time.Second) // 取消上下文 cancelFunc() // 讀取通道里面的數(shù)據(jù) res = <-resultChan // 打印結(jié)果 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í)行腳本并獲取結(jié)果
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...) // 顯示運(yùn)行的命令 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í)行包含的命令,但并不會(huì)等待該命令完成即返回 // wait方法會(huì)返回命令的返回狀態(tài)碼并在命令返回后釋放相關(guān)的資源 cmd.Start() reader := bufio.NewReader(stdout) var index int // 實(shí)時(shí)循環(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
到此這篇關(guān)于Go語(yǔ)言執(zhí)行cmd命令庫(kù)的方法實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go執(zhí)行cmd內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
GoFrame框架數(shù)據(jù)校驗(yàn)之校驗(yàn)結(jié)果Error接口對(duì)象
這篇文章主要為大家介紹了GoFrame框架數(shù)據(jù)校驗(yàn)之校驗(yàn)結(jié)果Error接口對(duì)象示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Golang?rabbitMQ生產(chǎn)者消費(fèi)者實(shí)現(xiàn)示例
這篇文章主要為大家介紹了Golang?rabbitMQ生產(chǎn)者消費(fèi)者實(shí)現(xiàn)的示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04Go語(yǔ)言設(shè)計(jì)模式之實(shí)現(xiàn)觀察者模式解決代碼臃腫
今天學(xué)習(xí)一下用?Go?實(shí)現(xiàn)觀察者模式,觀察者模式主要是用來(lái)實(shí)現(xiàn)事件驅(qū)動(dòng)編程。事件驅(qū)動(dòng)編程的應(yīng)用還是挺廣的,除了我們都知道的能夠用來(lái)解耦:用戶修改密碼后,給用戶發(fā)短信進(jìn)行風(fēng)險(xiǎn)提示之類的典型場(chǎng)景,在微服務(wù)架構(gòu)實(shí)現(xiàn)最終一致性、實(shí)現(xiàn)事件源A?+?ES2022-08-08使用go語(yǔ)言解析xml的實(shí)現(xiàn)方法(必看篇)
下面小編就為大家?guī)?lái)一篇使用go語(yǔ)言解析xml的實(shí)現(xiàn)方法(必看篇)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2017-06-06