在很多場(chǎng)合, 使用Go語(yǔ)言需要調(diào)用外部命令來(lái)完成一些特定的任務(wù), 例如: 使用Go語(yǔ)言調(diào)用Linux命令來(lái)獲取執(zhí)行的結(jié)果,又或者調(diào)用第三方程序執(zhí)行來(lái)完成額外的任務(wù)。在go的標(biāo)準(zhǔn)庫(kù)中, 專門(mén)提供了os/exec包來(lái)對(duì)調(diào)用外部程序提供支持, 本文將對(duì)調(diào)用外部命令常用的幾種場(chǎng)景進(jìn)行總結(jié)。
先用Linux上的一個(gè)簡(jiǎn)單命令執(zhí)行看一下效果, 執(zhí)行cal命令, 會(huì)打印當(dāng)前月的日期信息,如圖:
如果要使用Go代碼調(diào)用該命令, 可以使用以下代碼:
func main(){ cmd := exec.Command("cal") err := cmd.Run() if err != nil { fmt.Println(err.Error()) }}
首先, 調(diào)用"os/exec"包中的Command函數(shù),并傳入命令名稱作為參數(shù), Command函數(shù)會(huì)返回一個(gè)exec.Cmd的命令對(duì)象。接著調(diào)用該命令對(duì)象的Run()方法運(yùn)行命令。
如果此時(shí)運(yùn)行程序, 會(huì)發(fā)現(xiàn)什么都沒(méi)有出現(xiàn), 這是因?yàn)槲覀儧](méi)有處理標(biāo)準(zhǔn)輸出, 調(diào)用os/exec執(zhí)行命令, 標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤默認(rèn)會(huì)被丟棄。
這里將cmd結(jié)構(gòu)中的Stdout和Stderr分別設(shè)置為os.stdout和os.Stderr, 代碼如下:
func main(){ cmd := exec.Command("cal") cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { fmt.Println(err.Error()) }}
運(yùn)行程序后顯示:
輸出到文件的關(guān)鍵, 是將exec.Cmd對(duì)象的Stdout和Stderr賦值文件句柄, 代碼如下:
func main(){ f, err := os.OpenFile("sample.txt", os.O_WRONLY|os.O_CREATE, os.ModePerm) if err != nil { fmt.Println(err.Error()) } cmd := exec.Command("cal") cmd.Stdout = f cmd.Stderr = f err := cmd.Run() if err != nil { fmt.Println(err.Error()) }}
os.OpenFile打開(kāi)一個(gè)文件, 指定os.0_CREATE標(biāo)志讓操作系統(tǒng)在文件不存在時(shí)自動(dòng)創(chuàng)建, 返回文件對(duì)象*os.File, *os.File實(shí)現(xiàn)了io.Writer接口。
運(yùn)行程序結(jié)果如下:
這里開(kāi)啟一個(gè)HTTP服務(wù), 服務(wù)端接收兩個(gè)參數(shù):年和月, 在服務(wù)端通過(guò)執(zhí)行系統(tǒng)命令返回結(jié)果,代碼如下:
import ( "fmt" "net/http" "os/exec")func queryDate(w http.ResponseWriter, r *http.Request) { var err error if r.Method == "GET" { year := r.URL.Query().Get("year") month := r.URL.Query().Get("month") cmd := exec.Command("cal", month, year) cmd.Stdout = w cmd.Stderr = w err = cmd.Run() if err != nil { fmt.Println(err.Error()) } }}func main() { http.HandleFunc("/querydate", queryDate) http.ListenAndServe(":8001", nil)}
打開(kāi)瀏覽器,在地址欄中輸入U(xiǎn)RL查詢2023年10月份的日歷:http://localhost:8001/querydate?year=2023&mnotallow=10 , 結(jié)果如下:
如果要將執(zhí)行命令的結(jié)果同時(shí)輸出到文件、網(wǎng)絡(luò)和內(nèi)存對(duì)象, 可以使用io.MultiWriter滿足需求, io.MultiWriter可以很方便的將多個(gè)io.Writer轉(zhuǎn)換成一個(gè)io.Writer, 修改之前的Web服務(wù)端程序如下:
func queryDate(w http.ResponseWriter, r *http.Request) { var err error if r.Method == "GET" { buffer := bytes.NewBuffer(nil) year := r.URL.Query().Get("year") month := r.URL.Query().Get("month") f, _ := os.OpenFile("sample.txt", os.O_WRONLY|os.O_CREATE, os.ModePerm) mw := io.MultiWriter(w, f, buffer) cmd := exec.Command("cal", month, year) cmd.Stdout = mw cmd.Stderr = mw err = cmd.Run() if err != nil { fmt.Println(err.Error()) } fmt.Println(buffer.String()) }}func main() { http.HandleFunc("/querydate", queryDate) http.ListenAndServe(":8001", nil)}
這里我們封裝一個(gè)常用函數(shù), 輸入接收命令和多個(gè)參數(shù), 返回錯(cuò)誤和命令返回信息, 函數(shù)代碼如下:
func ExecCommandOneTimeOutput(name string, args ...string) (error, string) { var out bytes.Buffer var stderr bytes.Buffer cmd := exec.Command(name, args...) cmd.Stdout = &out cmd.Stderr = &stderr err := cmd.Run() if err != nil { fmt.Println(fmt.Sprint(err) + ": " + stderr.String()) return err, "" } return nil, out.String()}
該函數(shù)可以作為通用的命令執(zhí)行返回結(jié)果的函數(shù), 分別返回了錯(cuò)誤和命令返回信息。
在Linux系統(tǒng)中,有些命令運(yùn)行后結(jié)果是動(dòng)態(tài)持續(xù)更新的,例如: top命令,對(duì)于該場(chǎng)景,我們封裝函數(shù)如下:
func ExecCommandLoopTimeOutput(name string, args ...string) <-chan struct{} { cmd := exec.Command(name, args...) closed := make(chan struct{}) defer close(closed) stdoutPipe, err := cmd.StdoutPipe() if err != nil { fmt.Println(err.Error()) } defer stdoutPipe.Close() go func() { scanner := bufio.NewScanner(stdoutPipe) for scanner.Scan() { fmt.Println(string(scanner.Bytes())) _, err := simplifiedchinese.GB18030.NewDecoder().Bytes(scanner.Bytes()) if err != nil { continue } } }() if err := cmd.Run(); err != nil { fmt.Println(err.Error()) } return closed}
通過(guò)調(diào)用cmd對(duì)象的StdoutPipe()輸出管理函數(shù), 我們可以實(shí)現(xiàn)持續(xù)獲取后臺(tái)命令返回的結(jié)果,并保持程序不退出。
在調(diào)用該函數(shù)的時(shí)候, 調(diào)用方式如下:
<-ExecCommandLoopTimeOutput("top")
打印出的信息將是一個(gè)持續(xù)顯示信息,如圖:
本章節(jié)介紹了使用os/exec這個(gè)標(biāo)準(zhǔn)庫(kù)調(diào)用外部命令的各種場(chǎng)景。在實(shí)際應(yīng)用中, 基本用的最多的還是封裝好的:ExecCommandOneTimeOutput()和ExecCommandLoopTimeOutput()兩個(gè)函數(shù), 畢竟外部命令一般只會(huì)包含兩種:一種是執(zhí)行后馬上獲取結(jié)果,第二種就是常駐內(nèi)存持續(xù)獲取結(jié)果。
本文鏈接:http://www.www897cc.com/showinfo-26-59680-0.html在Go編程中調(diào)用外部命令的幾種場(chǎng)景
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com