go 語言調用 shell 命令有這麼兩種方式,都用過了?
阻塞方式 (需要執行結果)
主要用於執行shell命令,並且返回shell的標準輸出
適用於執行普通非阻塞 shell 命令,且需要 shell 標準輸出的
//阻塞式的執行外部shell命令的函數,等待執行完畢並返回標準輸出
func exec_shell(s string) (string, error){
//函數返回一個*Cmd,用於使用給出的參數執行name指定的程序
cmd := exec.Command("/bin/bash", "-c", s)
//讀取io.Writer類型的cmd.Stdout,再通過bytes.Buffer(緩衝byte類型的緩衝器)將byte類型轉化爲string類型(out.String():這是bytes類型提供的接口)
var out bytes.Buffer
cmd.Stdout = &out
//Run執行c包含的命令,並阻塞直到完成。 這裏stdout被取出,cmd.Wait()無法正確獲取stdin,stdout,stderr,則阻塞在那了
err := cmd.Run()
checkErr(err)
return out.String(), err
}
需要對 shell 標準輸出的逐行實時進行處理的
func execCommand(commandName string, params []string) bool {
//函數返回一個*Cmd,用於使用給出的參數執行name指定的程序
cmd := exec.Command(commandName, params...)
//顯示運行的命令
fmt.Println(cmd.Args)
//StdoutPipe方法返回一個在命令Start後與命令標準輸出關聯的管道。Wait方法獲知命令結束後會關閉這個管道,一般不需要顯式的關閉該管道。
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Println(err)
return false
}
cmd.Start()
//創建一個流來讀取管道內內容,這裏邏輯是通過一行一行的讀取的
reader := bufio.NewReader(stdout)
//實時循環讀取輸出流中的一行內容
for {
line, err2 := reader.ReadString('\n')
if err2 != nil || io.EOF == err2 {
break
}
fmt.Println(line)
}
//阻塞直到該命令執行完成,該命令必須是被Start方法開始執行的
cmd.Wait()
return true
}
非阻塞方式 (不需要執行結果)
通過shell調用自己的程序,並且程序是死循環,此時無法獲取返回結果 (否則程序會一直阻塞直至調用的 程序結束)
適用於調用自己寫的程序 (服務器死循環,且不需要返回結果的)
//不需要執行命令的結果與成功與否,執行命令馬上就返回
func exec_shell_no_result(command string) {
//處理啓動參數,通過空格分離 如:setsid /home/luojing/gotest/src/test_main/iwatch/test/while_little &
command_name_and_args := strings.FieldsFunc(command, splite_command)
//開始執行c包含的命令,但並不會等待該命令完成即返回
cmd.Start()
if err != nil {
fmt.Printf("%v: exec command:%v error:%v\n", get_time(), command, err)
}
fmt.Printf("Waiting for command:%v to finish...\n", command)
//阻塞等待fork出的子進程執行的結果,和cmd.Start()配合使用[不等待回收資源,會導致fork出執行shell命令的子進程變爲殭屍進程]
err = cmd.Wait()
if err != nil {
fmt.Printf("%v: Command finished with error: %v\n", get_time(), err)
}
return
}
錯誤處理函數
//錯誤處理函數
func checkErr(err error) {
if err != nil {
fmt.Println(err)
panic(err)
}
}
官網的標準中文庫
引用部分文檔
func (*Cmd) Run
func (c *Cmd) Run() error
Run執行 c 包含的命令,並阻塞直到完成。
如果命令成功執行,stdin、stdout、stderr的轉交沒有問題,並且返回狀態碼爲 0,方法的返回值爲nil;如果命令沒有執行或者執行失敗,會返回*ExitError類型的錯誤;否則返回的error可能是表示 I/O 問題。
func (*Cmd) Start
func (c *Cmd) Start() error
Start開始執行 c 包含的命令,但並不會等待該命令完成即返回。Wait方法會返回命令的返回狀態碼並在命令返回後釋放相關的資源。
func (*Cmd) Wait
func (c *Cmd) Wait() error
Wait會阻塞直到該命令執行完成,該命令必須是被Start方法開始執行的。
如果命令成功執行,stdin、stdout、stderr的轉交沒有問題,並且返回狀態碼爲 0,方法的返回值爲nil;如果命令沒有執行或者執行失敗,會返回*ExitError類型的錯誤;否則返回的error可能是表示 I/O 問題。Wait方法會在命令返回後釋放相關的資源。
func (*Cmd) Output
func (c *Cmd) Output() ([]byte, error)
執行命令並返回標準輸出的切片。
func (*Cmd) StderrPipe
func (c *Cmd) StderrPipe() (io.ReadCloser, error)
StderrPipe方法返回一個在命令Start後與命令標準錯誤輸出關聯的管道。Wait方法獲知命令結束後會關閉這個管道,一般不需要顯式的關閉該管道。但是在從管道讀取完全部數據之前調用Wait是錯誤的;同樣使用StderrPipe方法時調用 Run 函數也是錯誤的。
到此這篇關於 go 調用 shell 命令兩種方式實現 (有無返回值) 的文章就介紹到這了
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/clW7gl_hMTYtXZbnXI4_Pg