Go 学习笔记(43)— Go 标准库之 os/exec(执行外部命令、非阻塞等待、阻塞等待、命令输出)
1. 概述
golang 下的 os/exec 包執行外部命令包執行外部命令。它包裝了 os.StartProcess 函數以便更容易的修正輸入和輸出,使用管道連接I/O,以及作其它的一些調整。
與 C 語言或者其他語言中的“系統”庫調用不同, os/exec 包并不調用系統 shell ,也不展開任何 glob (正則匹配)模式,也不處理通常由 shell 完成的其他擴展、管道或重定向。
2. 相關函數
2.1 Variables
var ErrNotFound = errors.New("executable file not found in $PATH")
如果路徑搜索沒有找到可執行文件時,就會返回本錯誤。
2.2 type Error
type Error struct {Name stringErr  error
}
Error類型記錄執行失敗的程序名和失敗的原因。對應的方法如下:
func (e *Error) Error() string
2.3 type ExitError
type ExitError struct {*os.ProcessState
}
ExitError 報告某個命令的一次未成功的返回。對應的方法如下:
func (e *ExitError) Error() string
2.4 func LookPath
func LookPath(file string) (string, error)
2.5 type cmd
    func Command(name string, arg ...string) *Cmdfunc (c *Cmd) StdinPipe() (io.WriteCloser, error)func (c *Cmd) StdoutPipe() (io.ReadCloser, error)func (c *Cmd) StderrPipe() (io.ReadCloser, error)func (c *Cmd) Run() errorfunc (c *Cmd) Start() errorfunc (c *Cmd) Wait() errorfunc (c *Cmd) Output() ([]byte, error)func (c *Cmd) CombinedOutput() ([]byte, error)
3. 函數詳解
3.1 func LookPath
函數定義
func LookPath(file string) (string, error)
在環境變量 PATH 指定的目錄中搜索可執行文件,如 file 中有斜杠,則直接根據絕對路徑或者相對本目錄的相對路徑去查找。返回完整路徑或者相對于當前目錄的一個相對路徑。
默認在系統的環境變量里查找給定的可執行命令文件。
使用示例:
package mainimport ("fmt""os/exec"
)func main() {f, err := exec.LookPath("pwd")if err != nil {fmt.Println("Not find the cmd")}fmt.Printf("Binary file path is %s", f) // /bin/pwd
}3.2 Cmd 結構體
Cmd 代表一個正在準備或者在執行中的外部命令。
type Cmd struct {// Path是將要執行的命令的路徑。//// 該字段不能為空,如為相對路徑會相對于Dir字段。Path string// Args保管命令的參數,包括命令名作為第一個參數;如果為空切片或者nil,相當于無參數命令。//// 典型用法下,Path和Args都應被Command函數設定。Args []string// Env指定進程的環境,如為nil,則是在當前進程的環境下執行。Env []string// Dir指定命令的工作目錄。如為空字符串,會在調用者的進程當前目錄下執行。Dir string// Stdin指定進程的標準輸入,如為nil,進程會從空設備讀取(os.DevNull)Stdin io.Reader// Stdout和Stderr指定進程的標準輸出和標準錯誤輸出。//// 如果任一個為nil,Run方法會將對應的文件描述符關聯到空設備(os.DevNull)//// 如果兩個字段相同,同一時間最多有一個線程可以寫入。Stdout io.WriterStderr io.Writer// ExtraFiles指定額外被新進程繼承的已打開文件流,不包括標準輸入、標準輸出、標準錯誤輸出。// 如果本字段非nil,entry i會變成文件描述符3+i。//// BUG: 在OS X 10.6系統中,子進程可能會繼承不期望的文件描述符。// http://golang.org/issue/2603ExtraFiles []*os.File// SysProcAttr保管可選的、各操作系統特定的sys執行屬性。// Run方法會將它作為os.ProcAttr的Sys字段傳遞給os.StartProcess函數。SysProcAttr *syscall.SysProcAttr// Process是底層的,只執行一次的進程。Process *os.Process// ProcessState包含一個已經存在的進程的信息,只有在調用Wait或Run后才可用。ProcessState *os.ProcessState// 內含隱藏或非導出字段
}
注: exec 在執行調用系統命令時,會先對需要執行的操作進行一次封裝,然后再執行。封裝后的命令對象具有以上 struct 屬性。而封裝方式即使用下邊的 Command 函數。
3.3 func Command
func Command(name string, arg ...string) *Cmd
函數返回一個 *Cmd ,用于使用給出的參數執行 name 指定的程序。返回值只設定了 Path 和 Args 兩個參數。
 如果 name 不含路徑分隔符,將使用 LookPath 獲取完整路徑;否則直接使用 name 。參數 arg 不應包含命令名。
使用示例:
func main() {cmd := exec.Command("go", "version")fmt.Printf("cmd.Path is %s, cmd.Args is %s", cmd.Path, cmd.Args)// cmd.Path is /usr/local/go/bin/go, cmd.Args is [go version]
}
注意:以上操作只會將命令進行封裝,相當于告訴系統將進行哪些操作,但是執行時無法獲取相關信息。所以說Cmd 代表一個正在準備或者在執行中的外部命令。
3.4 func (*Cmd) Run
func (c *Cmd) Run() error
Run 執行 c 包含的命令,并阻塞直到完成。
- 如果命令成功執行, stdin、stdout、stderr的轉交沒有問題,并且返回狀態碼為 0,方法的返回值為nil;
- 如果命令沒有執行或者執行失敗,會返回 *ExitError類型的錯誤;否則返回的error可能是表示I/O問題。
使用示例:
func main() {cmd := exec.Command("go", "version")fmt.Printf("cmd.Path is %s, cmd.Args is %s", cmd.Path, cmd.Args)err := cmd.Run()if err != nil {fmt.Println("cmd run failed")}}
有輸出結果值:
command := "echo hello"
cmd := exec.Command("/bin/bash", "-c", command)
// cmd := exec.Command("sh", "-c", command)
bytes, err := cmd.Output()
if err != nil {log.Println(err)
}
resp := string(bytes)
log.Println(resp)
沒有輸出結果值:
command := "echo hello"
cmd := exec.Command("/bin/bash", "-c", command)
// cmd := exec.Command("sh", "-c", command)
err = cmd.Run()
3.5 func (*Cmd) Start
func (c *Cmd) Start() error
Start 開始執行 c 包含的命令,但并不會等待該命令完成即返回。 Wait 方法會返回命令的返回狀態碼并在命令返回后釋放相關的資源。
3.6 func (*Cmd) Wait
func (c *Cmd) Wait() error
Wait 會阻塞直到該命令執行完成,該命令必須是被 Start 方法開始執行的。
- 如果命令成功執行, stdin、stdout、stderr的轉交沒有問題,并且返回狀態碼為 0,方法的返回值為nil;
- 如果命令沒有執行或者執行失敗,會返回 *ExitError類型的錯誤;否則返回的error可能是表示I/O問題。Wait方法會在命令返回后釋放相關的資源。
使用示例:
func main() {cmd := exec.Command("go", "version")fmt.Printf("cmd.Path is %s, cmd.Args is %s", cmd.Path, cmd.Args)err := cmd.Start()if err != nil {fmt.Println("cmd run failed")}err = cmd.Wait()if err != nil {fmt.Println("cmd wait failed")}}
3.7 Start 和 Run 的區別
使用示例:
func main() {cmd := exec.Command("sleep", "5")// err := cmd.Run() 執行 Run 會立即阻塞等待 5 秒種err := cmd.Start()if err != nil {fmt.Println("cmd run failed")}fmt.Println("waiting for cmd ...")// 執行 Start() 上面的打印會先執行,然后在 Wait() 方法阻塞等待 5serr = cmd.Wait()if err != nil {fmt.Println("cmd wait failed")}}
注意:
 一個命令只能使用 Start() 或者 Run() 中的一個啟動命令,不能兩個同時使用。
3.8 func CombinedOutput
func (c *Cmd) CombinedOutput() ([]byte, error)
執行命令并返回標準輸出和錯誤輸出合并的切片。
3.9 func Output
func (c *Cmd) Output() ([]byte, error)
執行命令并返回標準輸出的切片。
使用示例:
func main() {cmd := exec.Command("ls", "-a", "-l")// err := cmd.Start()// if err != nil {// 	fmt.Println("cmd run failed")// }// fmt.Println("waiting for cmd ...")// err = cmd.Wait()// if err != nil {// 	fmt.Println("cmd wait failed")// }out, err := cmd.Output()if err != nil {fmt.Println("cmd Output failed ", err)}fmt.Printf("out is %s", out)
}
注意:
- Output()和- CombinedOutput()不能夠同時使用,因為- command的標準輸出只能有一個,同時使用的話便會定義了兩個,便會報錯;
- Command和- Output之間不需要再增加- Start、- Wait方法,否則也會報錯;
3.10 func (*Cmd) StdinPipe
func (c *Cmd) StdinPipe() (io.WriteCloser, error)
StdinPipe 方法返回一個在命令 Start 后與命令標準輸入關聯的管道。 Wait 方法獲知命令結束后會關閉這個管道。必要時調用者可以調用 Close 方法來強行關閉管道,例如命令在輸入關閉后才會執行返回時需要顯式關閉管道。
3.11 func (*Cmd) StdoutPipe
func (c *Cmd) StdoutPipe() (io.ReadCloser, error)
StdoutPipe 方法返回一個在命令 Start 后與命令標準輸出關聯的管道。 Wait 方法獲知命令結束后會關閉這個管道,一般不需要顯式的關閉該管道。但是在從管道讀取完全部數據之前調用 Wait 是錯誤的;同樣使用 StdoutPipe 方法時調用 Run 函數也是錯誤的。
3.12 func (*Cmd) StderrPipe
func (c *Cmd) StderrPipe() (io.ReadCloser, error)
StderrPipe 方法返回一個在命令 Start 后與命令標準錯誤輸出關聯的管道。 Wait 方法獲知命令結束后會關閉這個管道,一般不需要顯式的關閉該管道。但是在從管道讀取完全部數據之前調用 Wait 是錯誤的;同樣使用 StderrPipe 方法時調用 Run 函數也是錯誤的。
參考資料:
- https://blog.csdn.net/u013256816/article/details/99670090
- https://studygolang.com/static/pkgdoc/pkg/os_exec.htm
- https://www.cnblogs.com/sunailong/p/7852216.html
總結
以上是生活随笔為你收集整理的Go 学习笔记(43)— Go 标准库之 os/exec(执行外部命令、非阻塞等待、阻塞等待、命令输出)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 扣扣个性签名伤感男生
- 下一篇: 淘宝运费险多少钱啊?
