美文网首页
Go 执行命令行并实时打印输出

Go 执行命令行并实时打印输出

作者: 光剑书架上的书 | 来源:发表于2022-06-26 15:48 被阅读0次
    func main(){
        makeCmd := exec.Command("make")
        PrintCmdOutput(makeCmd)
    }
    
    func PrintCmdOutput(cmd *exec.Cmd) {
        cmd.Stdin = os.Stdin
    
        var wg sync.WaitGroup
        wg.Add(2)
        //捕获标准输出
        stdout, err := cmd.StdoutPipe()
        if err != nil {
            fmt.Println("INFO:", err)
            os.Exit(1)
        }
        readout := bufio.NewReader(stdout)
        go func() {
            defer wg.Done()
            GetOutput(readout)
        }()
    
        //捕获标准错误
        stderr, err := cmd.StderrPipe()
        if err != nil {
            fmt.Println("ERROR:", err)
            os.Exit(1)
        }
        readerr := bufio.NewReader(stderr)
        go func() {
            defer wg.Done()
            GetOutput(readerr)
        }()
    
        //执行命令
        err = cmd.Run()
        if err != nil {
            return
        }
        wg.Wait()
    }
    
    func GetOutput(reader *bufio.Reader) {
        var sumOutput string //统计屏幕的全部输出内容
        outputBytes := make([]byte, 200)
        for {
            n, err := reader.Read(outputBytes) //获取屏幕的实时输出(并不是按照回车分割,所以要结合sumOutput)
            if err != nil {
                if err == io.EOF {
                    break
                }
                fmt.Println(err)
                sumOutput += err.Error()
            }
            output := string(outputBytes[:n])
            fmt.Print(output) //输出屏幕内容
            sumOutput += output
        }
    }
    
    
    

    golang执行命令 && 实时获取输出结果

    背景

    golang可以获取命令执行的输出结果,但要执行完才能够获取。
    如果执行的命令是ssh,我们要实时获取,并执行相应的操作呢?
    示例

    func main() {
        user := "root"
        host := "172.16.116.133"
    
        //获取执行命令
        cmd := exec.Command("ssh", fmt.Sprintf("%s@%s", user, host))
        cmd.Stdin = os.Stdin
    
        var wg sync.WaitGroup
        wg.Add(2)
        //捕获标准输出
        stdout, err := cmd.StdoutPipe()
        if err != nil {
            fmt.Println("ERROR:", err)
            os.Exit(1)
        }
        readout := bufio.NewReader(stdout)
        go func() {
            defer wg.Done()
            GetOutput(readout)
        }()
    
        //捕获标准错误
        stderr, err := cmd.StderrPipe()
        if err != nil {
            fmt.Println("ERROR:", err)
            os.Exit(1)
        }
        readerr := bufio.NewReader(stderr)
        go func() {
            defer wg.Done()
            GetOutput(readerr)
        }()
    
        //执行命令
        cmd.Run()
        wg.Wait()
        return
    }
    func GetOutput(reader *bufio.Reader) {
        var sumOutput string                //统计屏幕的全部输出内容
        outputBytes := make([]byte, 200)
        for {
            n, err := reader.Read(outputBytes)      //获取屏幕的实时输出(并不是按照回车分割,所以要结合sumOutput)
            if err != nil {
                if err == io.EOF {
                    break
                }
                fmt.Println(err)
                sumOutput += err.Error()
            }
            output := string(outputBytes[:n])
            fmt.Print(output) //输出屏幕内容
            sumOutput += output
        }
        return
    }
    

    应用场景

    ssh是交互式命令,本示例实现了实时获取输出结果,并判断输出结果中有没有报错,报错则重试(再次登陆)。
    场景:本Demo只是把"错误"二字视为异常,然后重试,实际上比这复杂的多,比如ssh连接超时重试等,这个逻辑请自行补充。

    package main
    
    import (
        "bufio"
        "fmt"
        "io"
        "os"
        "os/exec"
        "strings"
        "sync"
        "time"
    )
    
    func main(){
        retryTimes := 3
        var retryInterval time.Duration = 3
        user := "root"
        host := "172.16.116.133"
    
        //部分场景下重试登录
        shouldRetry := true
        for i:=1;i<=retryTimes && shouldRetry;i++{
            //执行命令
            shouldRetry = RunSSHCommand(user,host)
            if !shouldRetry{
                return
            }
            time.Sleep(retryInterval * time.Second)
        }
        if shouldRetry{
            fmt.Println("\n失败,请重试或检查")
        }
    }
    func shouldRetryByOutput(output string)bool{
        if strings.Contains(output,"错误"){       //匹配到"错误"就重试.这里只是Demo,请根据实际情况设置。
            return true
        }
        return false
    }
    func GetAndFilterOutput(reader *bufio.Reader)(shouldRetry bool){
        var sumOutput string
        outputBytes:= make([]byte,200)
        for {
            n,err := reader.Read(outputBytes)
            if err!=nil{
                if err == io.EOF{
                    break
                }
                fmt.Println(err)
                sumOutput += err.Error()
            }
            output := string(outputBytes[:n])
            fmt.Print(output)       //输出屏幕内容
            sumOutput += output
            if shouldRetryByOutput(output){
                shouldRetry = true
            }
        }
        if shouldRetryByOutput(sumOutput){
            shouldRetry = true
        }
        return
    }
    func RunSSHCommand(user,host string)(shouldRetry bool){
        //获取执行命令
        cmd := exec.Command("ssh",fmt.Sprintf("%s@%s",user,host))
        cmd.Stdin = os.Stdin
    
        var wg sync.WaitGroup
        wg.Add(2)
        //捕获标准输出
        stdout, err := cmd.StdoutPipe()
        if err != nil {
            fmt.Println("ERROR:",err)
            os.Exit(1)
        }
        readout := bufio.NewReader(stdout)
        go func() {
            defer wg.Done()
            shouldRetryTemp := GetAndFilterOutput(readout)
            if shouldRetryTemp{
                shouldRetry = true
            }
        }()
    
        //捕获标准错误
        stderr, err := cmd.StderrPipe()
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
        readerr := bufio.NewReader(stderr)
        go func() {
            defer wg.Done()
            shouldRetryTemp := GetAndFilterOutput(readerr)
            if shouldRetryTemp{
                shouldRetry = true
            }
        }()
    
        //执行命令
        cmd.Run()
        wg.Wait()
        return
    }
    
    

    相关文章

      网友评论

          本文标题:Go 执行命令行并实时打印输出

          本文链接:https://www.haomeiwen.com/subject/telgvrtx.html