有時候項目需要實現一個類似shell腳本的功能,讓開發項目自身程序類型以外的其他腳本程序(或者編譯後程序)來根據情況執行,這個時候不會python怎麽辦?go語言可以幫助我們實現。先寫一些linux下shell命令的例子:
package main import( "fmt" "io/ioutil" "os/exec" ) func main() { // 從一個簡單的命令開始 這個命令不需要任何參數 // 或者輸入,僅僅向stdout輸出一些信息,使用包裡面的exec.Command dateCmd := exec.Command("date") // Output 是另一個運行命令時用來處理信息的函數,這個函數等待命令結束,然後收集命令輸出,如果沒有錯誤發生的話, // dateOut 將保存date的信息 dateOut, err := dateCmd.Output() if err != nil { panic(err) } fmt.Println("> date") fmt.Println(string(dateOut)) // 下面我們看一個需要從stdin輸入數據的命令, 我們將數據輸入傳給外部進程stdin // 然後從它輸出到stdout的運行結果收集信息 grepCmd := exec.Command("grep", "hello") // 這裡我們顯式地獲取input/output管道,啓動進程,向進程寫入數據,然後讀取輸出結果,最後等待進程結束 grepIn, _ := grepCmd.StdinPipe() grepOut, _ := grepCmd.StdoutPipe() grepCmd.Start() grepIn.Write([]byte("hello grep\ngoodbye grep")) grepIn.Close() grepBytes, _ := ioutil.ReadAll(grepOut) grepCmd.Wait() // 在上面的例子中,我開門忽略了錯誤檢測,但是你一樣可以使用 if err!=nil 這種模式來進行處理, // 另外,我們僅僅收集了stdoutPipe 的結果,同時你也可以用一樣的方法來收集StderrPipe的結果 fmt.Println("> grep hello") fmt.Println(string(grepBytes)) // 注意,在觸發外部命令的時候,需要顯式地提供命令和參數信息 // 另外如果你想用一個命令行字符串觸發一個完整的命令, 你可以使用bash的一個-c選項 lsCmd := exec.Command("bash", "-c", "ls -a -l -h") lsOut, err := lsCmd.Output() if err != nil { panic(err) } fmt.Println("> ls -a -l -h") fmt.Println(string(lsOut)) }
所觸發的程序執行結果和我們直接執行這些程序的結果是一樣的, 運行結果:
> date Sat Dec 8 11:18:35 PDT 2017 > grep hello hello grep > ls -a -l -h drwxr-xr-x 4 qinchao 136B Dec 3 10:10 . drwxr-xr-x 91 qinchao 3.0K Dec 3 10:10 .. -rw-r--r-- 1 qinchao 138K Dec 3 10:10 spawning-processes.go
一些新手遇到比較複雜連續的shell命令, 上來可能就開心地寫了以下的代碼
xxCmd := exec.Command("chmod -R 755 /root/sample") xxOut, err := xxCmd.Output() if err != nil { panic(err) }
發現會報錯誤no such file or directory
這裡糾正一下, 你首先要傳入bash執行路徑作爲第一個參數, 第二個參數傳入bash的執行參數, 一般都是-c, 最後的第三個參數才是傳shell指令。所以面對以上這種報錯解決方式, 應該這樣寫:
// 加入/bin/bash和-c作爲前兩個參數 xxCmd := exec.Command("/bin/bash", "-c", "chmod -R 777 /root/sample") xxOut, err := xxCmd.Output() if err != nil { panic(err) }