中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

golang如何調用shell命令

發布時間:2023-02-22 17:51:52 來源:億速云 閱讀:162 作者:iii 欄目:開發技術

這篇文章主要介紹了golang如何調用shell命令的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇golang如何調用shell命令文章都會有所收獲,下面我們一起來看看吧。

    普通用法(一次性獲取所有輸出)

    package main
    
    import (
        "fmt"
        "os/exec"
    )
    
    func main() {
        Command("ls")
    }
    
    // 這里為了簡化,我省去了stderr和其他信息
    func Command(cmd string) error {
        c := exec.Command("bash", "-c", cmd)
        // 此處是windows版本
        // c := exec.Command("cmd", "/C", cmd)
        output, err := c.CombinedOutput()
        fmt.Println(string(output))
        return err
    }

    可以看到,當前命令執行的是輸出當前目錄下的文件/文件夾

    golang如何調用shell命令

    實時顯示

    效果圖:

    golang如何調用shell命令

    package main
    
    import (
        "bufio"
        "fmt"
        "io"
        "os/exec"
        "sync"
    )
    
    func main() {
        // 執行ping baidu的命令, 命令不會結束
        Command("ping www.baidu.com")
    
    }
    
    func Command(cmd string) error {
        //c := exec.Command("cmd", "/C", cmd)   // windows
        c := exec.Command("bash", "-c", cmd)  // mac or linux
        stdout, err := c.StdoutPipe()
        if err != nil {
            return err
        }
        var wg sync.WaitGroup
        wg.Add(1)
        go func() {
            defer wg.Done()
            reader := bufio.NewReader(stdout)
            for {
                readString, err := reader.ReadString('\n')
                if err != nil || err == io.EOF {
                    return
                }
                fmt.Print(readString)
            }
        }()
        err = c.Start()
        wg.Wait()
        return err
    }

    可關閉+實時輸出

    package main
    
    import (
        "bufio"
        "context"
        "fmt"
        "io"
        "os/exec"
        "sync"
        "time"
    )
    
    func main() {
        ctx, cancel := context.WithCancel(context.Background())
        go func(cancelFunc context.CancelFunc) {
            time.Sleep(3 * time.Second)
            cancelFunc()
        }(cancel)
        Command(ctx, "ping www.baidu.com")
    }
    
    func Command(ctx context.Context, cmd string) error {
        // c := exec.CommandContext(ctx, "cmd", "/C", cmd)
        c := exec.CommandContext(ctx, "bash", "-c", cmd) // mac linux
        stdout, err := c.StdoutPipe()
        if err != nil {
            return err
        }
        var wg sync.WaitGroup
        wg.Add(1)
        go func(wg *sync.WaitGroup) {
            defer wg.Done()
            reader := bufio.NewReader(stdout)
            for {
                // 其實這段去掉程序也會正常運行,只是我們就不知道到底什么時候Command被停止了,而且如果我們需要實時給web端展示輸出的話,這里可以作為依據 取消展示
                select {
                // 檢測到ctx.Done()之后停止讀取
                case <-ctx.Done():
                    if ctx.Err() != nil {
                        fmt.Printf("程序出現錯誤: %q", ctx.Err())
                    } else {
                        fmt.Println("程序被終止")
                    }
                    return
                default:
                    readString, err := reader.ReadString('\n')
                    if err != nil || err == io.EOF {
                        return
                    }
                    fmt.Print(readString)
                }
            }
        }(&wg)
        err = c.Start()
        wg.Wait()
        return err
    }

    效果圖:

    golang如何調用shell命令

    可以看到輸出了3次(1秒1次)之后程序就被終止了,確切的說是讀取輸出流的循環結束了。

    執行Python腳本(阻塞)

    其實很簡單,只要python -u xxx.py這樣執行就可以了, -u參數

    簡單的說就是python的輸出是有緩存的,-u會強制往標準流輸出,當Python腳本阻塞的時候

    也不會拿不到輸出!

    其他

    "bash" 和"-c",據我的觀察,這2個參數代表在當前cmd窗口執行,而不加這2個參數,直接上shell的話,會啟動一個新窗口,目前觀察是stdout拿不到數據。

    仍有缺陷

    上面的命令可以解決大部分問題,但是獲取不到stderr的信息,所以我們需要改造一下。

    下面是輸出和錯誤一并輸出的實時讀取,類似于jenkins那種。

    package main
    
    import (
        "bufio"
        "context"
        "fmt"
        "io"
        "os/exec"
        "sync"
        "time"
    )
    
    func main() {
        ctx, cancel := context.WithCancel(context.Background())
        go func(cancelFunc context.CancelFunc) {
            time.Sleep(3 * time.Second)
            cancelFunc()
        }(cancel)
        Command(ctx, "ping www.baidu.com")
    }
    
    func read(ctx context.Context, wg *sync.WaitGroup, std io.ReadCloser) {
        reader := bufio.NewReader(std)
        defer wg.Done()
        for {
            select {
            case <-ctx.Done():
                return
            default:
                readString, err := reader.ReadString('\n')
                if err != nil || err == io.EOF {
                    return
                }
                fmt.Print(readString)
            }
        }
    }
    
    func Command(ctx context.Context, cmd string) error {
        //c := exec.CommandContext(ctx, "cmd", "/C", cmd) // windows
        c := exec.CommandContext(ctx, "bash", "-c", cmd) // mac linux
        stdout, err := c.StdoutPipe()
        if err != nil {
            return err
        }
        stderr, err := c.StderrPipe()
        if err != nil {
            return err
        }
        var wg sync.WaitGroup
        // 因為有2個任務, 一個需要讀取stderr 另一個需要讀取stdout
        wg.Add(2)
        go read(ctx, &wg, stderr)
        go read(ctx, &wg, stdout)
        // 這里一定要用start,而不是run 詳情請看下面的圖
        err = c.Start()
        // 等待任務結束
        wg.Wait()
        return err
    }

    golang如何調用shell命令

    windows輸出亂碼問題

    最后給一個解決windows亂碼的完整案例

    package main
    
    import (
        "bufio"
        "fmt"
        "io"
        "os/exec"
        "sync"
        "golang.org/x/text/encoding/simplifiedchinese"
    )
    
    type Charset string
    
    const (
        UTF8    = Charset("UTF-8")
        GB18030 = Charset("GB18030")
    )
    
    func main() {
        // 執行ping baidu的命令, 命令不會結束
        Command("ping www.baidu.com")
    
    }
    
    func Command(cmd string) error {
        //c := exec.Command("cmd", "/C", cmd)   // windows
        c := exec.Command("bash", "-c", cmd)  // mac or linux
        stdout, err := c.StdoutPipe()
        if err != nil {
            return err
        }
        var wg sync.WaitGroup
        wg.Add(1)
        go func() {
            defer wg.Done()
            reader := bufio.NewReader(stdout)
            for {
                readString, err := reader.ReadString('\n')
                if err != nil || err == io.EOF {
                    return
                }
                byte2String := ConvertByte2String([]byte(readString), "GB18030")
                fmt.Print(byte2String)
            }
        }()
        err = c.Start()
        wg.Wait()
        return err
    }
    
    func ConvertByte2String(byte []byte, charset Charset) string {
        var str string
        switch charset {
        case GB18030:
            var decodeBytes, _ = simplifiedchinese.GB18030.NewDecoder().Bytes(byte)
            str = string(decodeBytes)
        case UTF8:
            fallthrough
        default:
            str = string(byte)
        }
        return str
    }

    概述

    遠程執行命令有什么用?為什么要遠程執行命令? 如果你只有2,3臺服務器需要管理的時候,遠程執行命令確實沒有沒多大作用,你可以登錄到每臺服務器上去完成各種操作。 當你的服務器大于3臺的時候,遠程執行的命令的方式就可以大大提高你的生產力了。

    如果你有一個可以遠程執行命令的工具,那么就可以像操作單臺機器那樣操作多臺機器,機器越多,效率提高的越多。 遠程執行命令最常用的方法就是利用 SSH 協議,將命令發送到遠程機器上執行,并獲取返回結果。

    本文介紹如何使用 golang 實現遠程執行命令。

    一般命令

    所謂一般命令,就是在一定時間內會執行完的命令。比如 grep, cat 等等。 執行命令的步驟是:連接,執行,獲取結果

    連接

    連接包含了認證,可以使用 password 或者 sshkey 2種方式來認證。下面的示例為了簡單,使用了密碼認證的方式來完成連接。

    import (  
      "fmt"
      "time"
    
      "golang.org/x/crypto/ssh"
    )
    
    func connect(user, password, host string, port int) (*ssh.Session, error) {  
      var (
        auth         []ssh.AuthMethod
        addr         string
        clientConfig *ssh.ClientConfig
        client       *ssh.Client
        session      *ssh.Session
        err          error
      )
      // get auth method
      auth = make([]ssh.AuthMethod, 0)
      auth = append(auth, ssh.Password(password))
    
      clientConfig = &ssh.ClientConfig{
        User:    user,
        Auth:    auth,
        Timeout: 30 * time.Second,
      }
    
      // connet to ssh
      addr = fmt.Sprintf("%s:%d", host, port)
    
      if client, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
        return nil, err
      }
    
      // create session
      if session, err = client.NewSession(); err != nil {
        return nil, err
      }
    
      return session, nil
    }

    連接的方法很簡單,只要提供登錄主機的 用戶*, *密碼*, *主機名或者IP*, *SSH端口

    執行,命令獲取結果

    連接成功后,執行命令很簡單

    import (  
      "fmt"
      "log"
      "os"
      "time"
    
      "golang.org/x/crypto/ssh"
    )
    
    func main() {  
      session, err := connect("root", "xxxxx", "127.0.0.1", 22)
      if err != nil {
        log.Fatal(err)
      }
      defer session.Close()
    
      session.Run("ls /; ls /abc")
    }

    上面代碼運行之后,雖然命令正常執行了,但是沒有正常輸出的結果,也沒有異常輸出的結果。 要想顯示結果,需要將 session 的 Stdout 和 Stderr 重定向 修改 func main 為如下:

    func main() {  
      session, err := connect("root", "xxxxx", "127.0.0.1", 22)
      if err != nil {
        log.Fatal(err)
      }
      defer session.Close()
    
      session.Stdout = os.Stdout
      session.Stderr = os.Stderr
      session.Run("ls /; ls /abc")
    }

    這樣就能在屏幕上顯示正常,異常的信息了。

    交互式命令

    上面的方式無法遠程執行交互式命令,比如 top , 遠程編輯一個文件,比如 vi /etc/nginx/nginx.conf 如果要支持交互式的命令,需要當前的terminal來接管遠程的 PTY。

    func main() {  
      session, err := connect("root", "olordjesus", "dockers.iotalabs.io", 2210)
      if err != nil {
        log.Fatal(err)
      }
      defer session.Close()
    
      fd := int(os.Stdin.Fd())
      oldState, err := terminal.MakeRaw(fd)
      if err != nil {
        panic(err)
      }
      defer terminal.Restore(fd, oldState)
    
      // excute command
      session.Stdout = os.Stdout
      session.Stderr = os.Stderr
      session.Stdin = os.Stdin
    
      termWidth, termHeight, err := terminal.GetSize(fd)
      if err != nil {
        panic(err)
      }
    
      // Set up terminal modes
      modes := ssh.TerminalModes{
        ssh.ECHO:          1,     // enable echoing
        ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
        ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
      }
    
      // Request pseudo terminal
      if err := session.RequestPty("xterm-256color", termHeight, termWidth, modes); err != nil {
        log.Fatal(err)
      }
    
      session.Run("top")
    }

    這樣就可以執行交互式命令了,比如上面的 top 也可以通過 vi /etc/nginx/nignx.conf 之類的命令來遠程編輯文件。

    關于“golang如何調用shell命令”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“golang如何調用shell命令”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    安塞县| 靖远县| 云霄县| 西乌珠穆沁旗| 临高县| 镇安县| 环江| 双柏县| 辉县市| 上饶市| 南靖县| 漾濞| 南阳市| 时尚| 东台市| 金昌市| 鄂伦春自治旗| 台北市| 松阳县| 曲麻莱县| 宁陵县| 冕宁县| 土默特左旗| 榕江县| 兴海县| 昌平区| 东光县| 航空| 玉屏| 富民县| 夹江县| 广饶县| 会昌县| 乌兰浩特市| 垣曲县| 三都| 会同县| 新巴尔虎右旗| 铜陵市| 庆阳市| 神农架林区|