久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長(zhǎng)資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      Go執(zhí)行腳本命令的使用實(shí)例以及源碼解析

      本文由golang教程欄目給大家介紹Go執(zhí)行腳本命令用例及源碼解析,希望對(duì)需要的朋友有所幫助!

      簡(jiǎn)介

      在開(kāi)發(fā)中我們可能會(huì)遇到需要在程序中調(diào)用腳本的需求,或者涉及到兩個(gè)語(yǔ)言之間的交互,筆者之前就遇到了需要在go中調(diào)用python的需求,然后在代碼中應(yīng)用了go-python3這個(gè)庫(kù),實(shí)際上在go中調(diào)用python的腳本也是一個(gè)解決之法。這片文章將介紹在go中運(yùn)行shell腳本的方法以及對(duì)其源碼的相應(yīng)解析。

      程序用例

      test_command.go

      package learnimport (    "fmt"    "os/exec"    "testing")func TestCmd(t *testing.T) {    if o, e := exec.Command("./test.sh", "1", "2").Output(); e != nil {       fmt.Println(e)    } else {       fmt.Println(string(o))    }}

      test.sh

      #!/bin/basha=$1b=$2echo $aecho $b

      上面這個(gè)例子的意思是要運(yùn)行test.sh這個(gè)腳本,并且入?yún)⑹?,2。腳本里面寫(xiě)的東西相對(duì)就比較簡(jiǎn)單了,就是打印這兩個(gè)入?yún)?。其?shí)問(wèn)題的關(guān)鍵在于exec.Command()這個(gè)方法,下面我們來(lái)刨根問(wèn)底,一探究竟。

      源碼解析

      func Command(name string, arg ...string) *Cmd {    cmd := &Cmd{       Path: name,       Args: append([]string{name}, arg...),    }    if filepath.Base(name) == name {       if lp, err := LookPath(name); err != nil {          cmd.lookPathErr = err      } else {          cmd.Path = lp      }    }    return cmd}// Base返回path的最后一個(gè)元素。// 在提取最后一個(gè)元素之前,將刪除尾部的路徑分隔符。// 如果路徑為空,Base返回"."。// 如果路徑完全由分隔符組成,Base返回單個(gè)分隔符。func Base(path string) string {    if path == "" {       return "."    }    // Strip trailing slashes.    for len(path) > 0 && os.IsPathSeparator(path[len(path)-1]) {       path = path[0 : len(path)-1]    }    // Throw away volume name    path = path[len(VolumeName(path)):]    // Find the last element    i := len(path) - 1    for i >= 0 && !os.IsPathSeparator(path[i]) {       i--    }    if i >= 0 {       path = path[i+1:]    }    // If empty now, it had only slashes.    if path == "" {       return string(Separator)    }    return path}//LookPath在由PATH環(huán)境變量命名的目錄中搜索一個(gè)名為file入?yún)⒌目蓤?zhí)行文件。如果文件包含一個(gè)斜線,就會(huì)直接嘗試,而不參考PATH。其結(jié)果可能是一個(gè)絕對(duì)路徑或相對(duì)于當(dāng)前目錄的路徑。func LookPath(file string) (string, error) {    if strings.Contains(file, "/") {       err := findExecutable(file)       if err == nil {          return file, nil       }       return "", &Error{file, err}    }    path := os.Getenv("PATH")    for _, dir := range filepath.SplitList(path) {       if dir == "" {          // Unix shell semantics: path element "" means "."          dir = "."       }       path := filepath.Join(dir, file)       if err := findExecutable(path); err == nil {          return path, nil       }    }    return "", &Error{file, ErrNotFound}}// 尋找file同名的可執(zhí)行命令func findExecutable(file string) error {    d, err := os.Stat(file)    if err != nil {       return err   }    if m := d.Mode(); !m.IsDir() && m&0111 != 0 {       return nil    }    return os.ErrPermission}

      通過(guò)上面對(duì)exec.Command()源碼的分析我們可以得知,這個(gè)函數(shù)只是尋找與path名字相同的可執(zhí)行文件并且構(gòu)建了一個(gè)Cmd的對(duì)象返回。這里值得注意的是,當(dāng)我們輸入的path如果不是一個(gè)可執(zhí)行的文件的具體路徑,那么就會(huì)去PATH環(huán)境變量中的注冊(cè)的路徑中找尋與path相同名字的命令,如果這個(gè)時(shí)候沒(méi)有找到就會(huì)報(bào)錯(cuò)。

      那么接下來(lái)我們那看看這個(gè)Cmd是何方神圣呢,有什么用,怎么用呢。下面我們看看Cmd這個(gè)結(jié)構(gòu)體里都有些什么東西。

      // Cmd結(jié)構(gòu)體代表一個(gè)準(zhǔn)備或正在執(zhí)行的外部命令// 一個(gè)Cmd的對(duì)象不能在Run,Output或者CombinedOutput方法調(diào)用之后重復(fù)使用。type Cmd struct {    // Path代表運(yùn)行命令的路徑    // 這個(gè)字段是唯一一個(gè)需要被賦值的字段,不能是空字符串,    // 并且如果Path是相對(duì)路徑,那么參照的是Dir這個(gè)字段的所指向的目錄    Path string     // Args這個(gè)字段代表調(diào)用命令所需的參數(shù),其中Path在運(yùn)行命令時(shí)以Args[0]的形式存在    // 如果這個(gè)參數(shù)是空,那個(gè)就直接使用Path運(yùn)行命令    //    // 在較為普遍普遍的場(chǎng)景里面,Path和Args這兩個(gè)參數(shù)在調(diào)用命令的時(shí)候都會(huì)被用到    Args []string     // Env代表當(dāng)前進(jìn)程的環(huán)境變量    // 每個(gè)Env數(shù)組中的條目都以“key=value”的形式存在    // 如果Env是nil,那邊運(yùn)行命令所創(chuàng)建的進(jìn)程將使用當(dāng)前進(jìn)程的環(huán)境變量    // 如果Env中存在重復(fù)的key,那么會(huì)使用這個(gè)key中排在最后一個(gè)的值。    // 在Windows中存在特殊的情況, 如果系統(tǒng)中缺失了SYSTEMROOT,或者這個(gè)環(huán)境變量沒(méi)有被設(shè)置成空字符串,那么它操作都是追加操作。    Env []string     // Dir代表命令的運(yùn)行路徑    // 如果Dir是空字符串,那么命令就會(huì)運(yùn)行在當(dāng)前進(jìn)程的運(yùn)行路徑    Dir string     // Stdin代表的是系統(tǒng)的標(biāo)準(zhǔn)輸入流    // 如果Stdin是一個(gè)*os.File,那么進(jìn)程的標(biāo)準(zhǔn)輸入將被直接連接到該文件。    Stdin io.Reader   // Stdout表示標(biāo)準(zhǔn)輸出流    // 如果StdOut是一個(gè)*os.File,那么進(jìn)程的標(biāo)準(zhǔn)輸入將被直接連接到該文件。    // 值得注意的是如果StdOut和StdErr是同一個(gè)對(duì)象,那么同一時(shí)間只有一個(gè)協(xié)程可以調(diào)用Writer    Stdout io.Writer    Stderr io.Writer   // ExtraFiles指定由新進(jìn)程繼承的額外開(kāi)放文件。它不包括標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出或標(biāo)準(zhǔn)錯(cuò)誤。如果不為零,第i項(xiàng)成為文件描述符3+i。    // ExtraFiles前面三個(gè)元素分別放的是stdin,stdout,stderr    // ExtraFiles在Windows上是不支持的    ExtraFiles []*os.File     SysProcAttr *syscall.SysProcAttr   // 當(dāng)命令運(yùn)行之后,Process就是該命令運(yùn)行所代表的進(jìn)程    Process *os.Process   // ProcessState包含關(guān)于一個(gè)退出的進(jìn)程的信息,在調(diào)用Wait或Run后可用。    ProcessState *os.ProcessState     ctx             context.Context // ctx可以用來(lái)做超時(shí)控制    lookPathErr     error           // 如果在調(diào)用LookPath尋找路徑的時(shí)候出錯(cuò)了,就賦值到這個(gè)字段    finished        bool                // 當(dāng)Wait被調(diào)用了一次之后就會(huì)被設(shè)置成True,防止被重復(fù)調(diào)用         childFiles      []*os.File    closeAfterStart []io.Closer    closeAfterWait  []io.Closer    goroutine       []func() error  //一系列函數(shù),在調(diào)用Satrt開(kāi)始執(zhí)行命令的時(shí)候會(huì)順帶一起執(zhí)行這些函數(shù)。每個(gè)函數(shù)分配一個(gè)goroutine執(zhí)行    errch           chan error             // 與上一個(gè)字段聯(lián)合使用,通過(guò)這個(gè)chan將上面函數(shù)執(zhí)行的結(jié)果傳到當(dāng)前goroutine    waitDone        chan struct{}}

      上面我們對(duì)Cmd這個(gè)結(jié)構(gòu)體的一些字段做了解析,可以理解為Cmd就是對(duì)一個(gè)命令生命周期內(nèi)的抽象。下面我們來(lái)分析Cmd的一下方法,看看他是怎么使用的。

      // Run方法開(kāi)始執(zhí)行這個(gè)命令并等待它運(yùn)行結(jié)束// 如果命令運(yùn)行,在復(fù)制stdin、stdout和stder時(shí)沒(méi)有問(wèn)題,并且以零退出狀態(tài)退出,則返回的錯(cuò)誤為nil。// 如果命令啟動(dòng)但沒(méi)有成功完成,錯(cuò)誤類型為類型為*ExitError。在其他情況下可能會(huì)返回其他錯(cuò)誤類型。// 如果調(diào)用的goroutine已經(jīng)用runtime.LockOSThread鎖定了操作系統(tǒng)線程,并修改了任何可繼承的OS級(jí) 線程狀態(tài)(例如,Linux或Plan 9名稱空間),新的 進(jìn)程將繼承調(diào)用者的線程狀態(tài)。func (c *Cmd) Run() error {    if err := c.Start(); err != nil {       return err   }    return c.Wait()}// Start方法啟動(dòng)指定的命令,但不等待它完成。//// 如果Start成功返回,c.Process字段將被設(shè)置。//// 一旦命令運(yùn)行完成,Wait方法將返回退出代碼并釋放相關(guān)資源。func (c *Cmd) Start() error {     if c.lookPathErr != nil {         c.closeDescriptors(c.closeAfterStart)         c.closeDescriptors(c.closeAfterWait)         return c.lookPathErr    }     if runtime.GOOS == "windows" {         lp, err := lookExtensions(c.Path, c.Dir)         if err != nil {             c.closeDescriptors(c.closeAfterStart)             c.closeDescriptors(c.closeAfterWait)             return err        }         c.Path = lp    }     if c.Process != nil {         return errors.New("exec: already started")     }     if c.ctx != nil {         select {         case <-c.ctx.Done():             c.closeDescriptors(c.closeAfterStart)             c.closeDescriptors(c.closeAfterWait)             return c.ctx.Err()         default:         }     }    //初始化并填充ExtraFiles     c.childFiles = make([]*os.File, 0, 3+len(c.ExtraFiles))     type F func(*Cmd) (*os.File, error)   //在這里會(huì)調(diào)用stdin,stdout和stderr方法,如果Cmd的StdIn,StdOut,StdErr不是nil,就會(huì)將相關(guān)的copy任務(wù)封裝成func放在goroutine字段中,等待在Start方法執(zhí)行的時(shí)候調(diào)用。     for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {         fd, err := setupFd(c)         if err != nil {             c.closeDescriptors(c.closeAfterStart)             c.closeDescriptors(c.closeAfterWait)             return err        }         c.childFiles = append(c.childFiles, fd)     }     c.childFiles = append(c.childFiles, c.ExtraFiles...)    // 如果cmd的Env沒(méi)有賦值,那么就用當(dāng)前進(jìn)程的環(huán)境變量     envv, err := c.envv()     if err != nil {         return err    }    // 會(huì)用這個(gè)命令啟動(dòng)一個(gè)新的進(jìn)程   // 在Linux的系統(tǒng)上,底層是調(diào)用了Frok來(lái)創(chuàng)建另一個(gè)進(jìn)程,由于文章篇幅有限,就不對(duì)此處進(jìn)行詳細(xì)分析了,詳情可看延伸閱讀     c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{         Dir:   c.Dir,         Files: c.childFiles,         Env:   addCriticalEnv(dedupEnv(envv)),         Sys:   c.SysProcAttr,     })     if err != nil {         c.closeDescriptors(c.closeAfterStart)         c.closeDescriptors(c.closeAfterWait)         return err    }      c.closeDescriptors(c.closeAfterStart)      // 除非有g(shù)oroutine要啟動(dòng),否則不會(huì)申請(qǐng)Chan     if len(c.goroutine) > 0 {         c.errch = make(chan error, len(c.goroutine))         for _, fn := range c.goroutine {             go func(fn func() error) {                 c.errch <- fn()             }(fn)         }     }    // 超時(shí)控制     if c.ctx != nil {         c.waitDone = make(chan struct{})         go func() {             select {             case <-c.ctx.Done(): //如果超時(shí)了,就Kill掉執(zhí)行命令的進(jìn)程                 c.Process.Kill()             case <-c.waitDone:             }         }()     }      return nil}func (c *Cmd) stdin() (f *os.File, err error) {     if c.Stdin == nil {         f, err = os.Open(os.DevNull)         if err != nil {             return         }         c.closeAfterStart = append(c.closeAfterStart, f)         return     }      if f, ok := c.Stdin.(*os.File); ok {         return f, nil     }    //Pipe返回一對(duì)相連的Files;從r讀出的數(shù)據(jù)返回寫(xiě)到w的字節(jié)。     pr, pw, err := os.Pipe()     if err != nil {         return     }      c.closeAfterStart = append(c.closeAfterStart, pr)     c.closeAfterWait = append(c.closeAfterWait, pw)   //將相關(guān)的任務(wù)添加到goroutine中     c.goroutine = append(c.goroutine, func() error {         _, err := io.Copy(pw, c.Stdin)         if skip := skipStdinCopyError; skip != nil && skip(err) {             err = nil         }         if err1 := pw.Close(); err == nil {             err = err1        }         return err    })     return pr, nil}func (c *Cmd) stdout() (f *os.File, err error) {     return c.writerDescriptor(c.Stdout)}func (c *Cmd) stderr() (f *os.File, err error) {     if c.Stderr != nil && interfaceEqual(c.Stderr, c.Stdout) {         return c.childFiles[1], nil     }     return c.writerDescriptor(c.Stderr)}func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) {     if w == nil {         f, err = os.OpenFile(os.DevNull, os.O_WRONLY, 0)         if err != nil {             return         }         c.closeAfterStart = append(c.closeAfterStart, f)         return     }      if f, ok := w.(*os.File); ok {         return f, nil     }      pr, pw, err := os.Pipe()     if err != nil {         return     }      c.closeAfterStart = append(c.closeAfterStart, pw)     c.closeAfterWait = append(c.closeAfterWait, pr)   //將相關(guān)的任務(wù)添加到goroutine中     c.goroutine = append(c.goroutine, func() error {         _, err := io.Copy(w, pr)         pr.Close() // in case io.Copy stopped due to write error         return err    })     return pw, nil}// 等待命令退出,并等待任何復(fù)制到stdin或從stdout或stderr復(fù)制的完成。// 在調(diào)用Wait之前,Start方法必須被調(diào)用// 如果命令運(yùn)行,在復(fù)制stdin、stdout和stder時(shí)沒(méi)有問(wèn)題,并且以零退出狀態(tài)退出,則返回的錯(cuò)誤為nil。// 如果命令運(yùn)行失敗或沒(méi)有成功完成,錯(cuò)誤類型為*ExitError。對(duì)于I/O問(wèn)題可能會(huì)返回其他錯(cuò)誤類型。// 如果c.Stdin、c.Stdout或c.Stderr中的任何一個(gè)不是*os.File,Wait也會(huì)等待各自的I/O循環(huán)復(fù)制到進(jìn)程中或從進(jìn)程中復(fù)制出來(lái)//// Wait釋放與Cmd相關(guān)的任何資源。func (c *Cmd) Wait() error {     if c.Process == nil {         return errors.New("exec: not started")     }     if c.finished {         return errors.New("exec: Wait was already called")     }     c.finished = true    //等待進(jìn)程運(yùn)行完畢并退出     state, err := c.Process.Wait()     if c.waitDone != nil {         close(c.waitDone)     }     c.ProcessState = state  //檢查goroutine字段上面的函數(shù)運(yùn)行有沒(méi)有錯(cuò)誤     var copyError error     for range c.goroutine {         if err := <-c.errch; err != nil && copyError == nil {             copyError = err        }     }      c.closeDescriptors(c.closeAfterWait)      if err != nil {         return err    } else if !state.Success() {         return &ExitError{ProcessState: state}     }      return copyError}// 輸出運(yùn)行該命令并返回其標(biāo)準(zhǔn)輸出。// 任何返回的錯(cuò)誤通常都是*ExitError類型的。// OutPut實(shí)際上是封裝了命令的執(zhí)行流程并且制定了命令的輸出流func (c *Cmd) Output() ([]byte, error) {     if c.Stdout != nil {         return nil, errors.New("exec: Stdout already set")     }     var stdout bytes.Buffer     c.Stdout = &stdout      captureErr := c.Stderr == nil     if captureErr {         c.Stderr = &prefixSuffixSaver{N: 32 << 10}     }      err := c.Run()     if err != nil && captureErr {         if ee, ok := err.(*ExitError); ok {             ee.Stderr = c.Stderr.(*prefixSuffixSaver).Bytes()         }     }     return stdout.Bytes(), err}

      在上面的方法分析之中我們可以看出運(yùn)行一個(gè)命令的流程是Run-> Start->Wait,等待命令運(yùn)行完成。并且在Start的時(shí)候會(huì)起來(lái)一個(gè)新的進(jìn)程來(lái)執(zhí)行命令。基于上面我們對(duì)Cmd的一頓分析,筆者感覺(jué)在文章開(kāi)頭寫(xiě)的測(cè)試代碼實(shí)在是乏善可陳,因?yàn)镃md封裝了挺多東西的,我們?cè)诠ぷ髦型耆梢猿浞掷盟庋b的功能,比如設(shè)置超時(shí)時(shí)間,設(shè)置標(biāo)準(zhǔn)輸入流或者標(biāo)準(zhǔn)輸出流,還可以定制化設(shè)置這個(gè)命令執(zhí)行的環(huán)境變量等等。。。?!?/p>

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)