Go基礎篇【第5篇】: 內置庫模塊 exec


Package exec runs external commands. It wraps os.StartProcess to make it easier to remap stdin and stdout, connect I/O with pipes, and do other adjustments.

Unlike the "system" library call from C and other languages, the os/exec package intentionally does not invoke the system shell and does not expand any glob patterns or handle other expansions, pipelines, or redirections typically done by shells. The package behaves more like C's "exec" family of functions. To expand glob patterns, either call the shell directly, taking care to escape any dangerous input, or use the path/filepath package's Glob function. To expand environment variables, use package os's ExpandEnv.

Note that the examples in this package assume a Unix system. They may not run on Windows, and they do not run in the Go Playground used by golang.org and godoc.org.

func LookPath

func LookPath(file string) (string, error)

在環境變量PATH指定的目錄中搜索可執行文件,如file中有斜杠,則只在當前目錄搜索。

即:默認在系統的環境變量里查找給定的可執行命令文件,如果查找到返回路徑,否則報錯,unix是$PATH,windows是$PATH$。可提供相對路徑下進行查找,並返回相對路徑

type 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.Writer
    Stderr io.Writer
    // ExtraFiles指定額外被新進程繼承的已打開文件流,不包括標准輸入、標准輸出、標准錯誤輸出。
    // 如果本字段非nil,entry i會變成文件描述符3+i。
    //
    // BUG: 在OS X 10.6系統中,子進程可能會繼承不期望的文件描述符。
    // http://golang.org/issue/2603
    ExtraFiles []*os.File
    // SysProcAttr保管可選的、各操作系統特定的sys執行屬性。
    // Run方法會將它作為os.ProcAttr的Sys字段傳遞給os.StartProcess函數。
    SysProcAttr *syscall.SysProcAttr
    // Process是底層的,只執行一次的進程。
    Process *os.Process
    // ProcessState包含一個已經存在的進程的信息,只有在調用Wait或Run后才可用。
    ProcessState *os.ProcessState
    // 內含隱藏或非導出字段
}

Cmd代表一個正在准備或者在執行中的外部命令。

注:exec在執行調用系統命令時,會先對需要執行的操作進行一次封裝,然后在執行。封裝后的命令對象具有以上struct屬性。而封裝方式即使用下邊的command函數。

func Command

func Command(name string, arg ...string) *Cmd

函數返回一個*Cmd,用於使用給出的參數執行name指定的程序。返回值只設定了Path和Args兩個參數。

如果name不含路徑分隔符(如果不是相對路徑),將使用LookPath獲取完整路徑(就是用默認的全局變量路徑);否則直接使用name。參數arg不應包含命令名。

cmd := exec.Command("go","version")
fmt.Println(cmd.Args, cmd.Path)

注:在調用命令執行封裝時,如果不提供相對路徑,系統會使用LookPath獲取完整路徑;即這里可以給一個相對路徑。

以上操作只會將命令進行封裝,相當於告訴系統將進行哪些操作,但是執行時無法獲取相關信息,因此我們還需要連接到命令執行時相關的輸入輸出pipe。################################################################################################

我們可以通過指定一個對象連接到對應的管道進行傳輸參數(stdinpipe),獲取輸出(stdoutpipe),獲取錯誤(stderrpipe)

func (*Cmd) StdinPipe

func (c *Cmd) StdinPipe() (io.WriteCloser, error)  //err 返回的是執行函數時的錯誤
package main

import (
    "fmt"
    "os"
    "os/exec"
)

func main() {
    cmd := exec.Command("cat")
    stdin, err := cmd.StdinPipe()       //指定stdin連接StdinPipe,然后操作stdin就實現了對command的參數傳遞
    if err != nil {
        fmt.Println(err)
    }
    _, err = stdin.Write([]byte("tmp.txt"))   //字節切片
    if err != nil {
        fmt.Println(err)
    }
    stdin.Close()
    cmd.Stdout = os.Stdout     //終端標准輸出tmp.txt
    cmd.Start()
}

StdinPipe方法返回一個在命令Start后與命令標准輸入關聯的管道。Wait方法獲知命令結束后會關閉這個管道。必要時調用者可以調用Close方法來強行關閉管道,例如命令在輸入關閉后才會執行返回時需要顯式關閉管道。

func (*Cmd) StdoutPipe

func (c *Cmd) StdoutPipe() (io.ReadCloser, error)    //err 返回的是執行函數時的錯誤
func main() {
    cmd := exec.Command("ls")
    stdout, err := cmd.StdoutPipe()  //指向cmd命令的stdout,然后就可以從stdout讀出信息
    cmd.Start()
    content, err := ioutil.ReadAll(stdout)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(string(content))     //輸出ls命令查看到的內容
}

StdoutPipe方法返回一個在命令Start后與命令標准輸出關聯的管道。Wait方法獲知命令結束后會關閉這個管道,一般不需要顯式的關閉該管道。但是在從管道讀取完全部數據之前調用Wait是錯誤的;同樣使用StdoutPipe方法時調用Run函數也是錯誤的。

func (*Cmd) StderrPipe

func (c *Cmd) StderrPipe() (io.ReadCloser, error)   //err 返回的是執行函數時的錯誤
import (
"fmt"
"io/ioutil"
"os/exec"
)

func main() {
    c := exec.Command("mv", "hello")
    i, err := c.StderrPipe()
    if err != nil {
        fmt.Printf("Error: %s\n", err)
        return
    }
    if err = c.Start(); err != nil {    //表示命令正確執行了
        fmt.Printf("Error: %s\n", err)
    }
    b, _ := ioutil.ReadAll(i)        //讀取命令執行的返回結果(命令執行結果的錯誤信息)
    if err := c.Wait(); err != nil {
        fmt.Printf("Error: %s\n", err)   //Error: exit status 1 mv: missing file argument Try `mv --help' for more information.
    }                                                                 
    fmt.Println(string(b))
}

StderrPipe方法返回一個在命令Start后與命令標准錯誤輸出關聯的管道。Wait方法獲知命令結束后會關閉這個管道,一般不需要顯式的關閉該管道。但是在從管道讀取完全部數據之前調用Wait是錯誤的;同樣使用StderrPipe方法時調用Run函數也是錯誤的

#################################################################

func (*Cmd) Run 

func (c *Cmd) Run() error

Run執行c包含的命令,並阻塞直到完成。

如果命令成功執行,stdin、stdout、stderr的轉交沒有問題,並且返回狀態碼為0,方法的返回值為nil【執行Run函數的返回狀態,正確執行Run函數,並不代表正確執行了命令】;如果函數沒有執行或者執行失敗,會返回*ExitError類型的錯誤;否則返回的error可能是表示I/O問題。

即:該命令只會執行且阻塞到執行結束,如果執行函數有錯則返回報錯信息,沒錯則返回nil,並不會返回執行結果。

func (*Cmd) Start

func (c *Cmd) Start() error

Start開始執行c包含的命令,但並不會等待該命令完成即返回。Wait方法會返回命令的返回狀態碼並在命令返回后釋放相關的資源。

Example

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)

執行命令並返回標准輸出的切片。不用通過pipe方式獲取命令的執行結果

import (
"fmt"
"os/exec"
)

func main() {
    c, err := exec.Command("date").Output() if err != nil {
        fmt.Println(err)
    }
    fmt.Println(string(c)) //    Sat Jan 4 17:07:36 2014 這個是標准庫里的例子
}

func (*Cmd) CombinedOutput

func (c *Cmd) CombinedOutput() ([]byte, error)

執行命令並返回標准輸出和錯誤輸出合並的切片。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM