美文网首页
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 执行命令行并实时打印输出

    golang执行命令 && 实时获取输出结果 背景 golang可以获取命令执行的输出结果,但要执行完才能够获取。...

  • Go命令

    假如你已安装了golang环境,你可以在命令行执行go命令查看相关的Go语言命令: go env用于打印Go语言的...

  • go Flag应用

    go语言些命令行程序是不错的选择,所以它可以轻松达到如上图类似nginx命令行的效果 例子 先执行go build...

  • Break Continue Return

    1.break 打印输出 当执行break时,会结束循环. 2.continue 打印输出 当执行continue...

  • Newman

    一、Newman:Postman 提供的命令行执行工具,可以在命令行中执行 Collection 运行过程,并生成...

  • GoLand报错:package XXX is not in G

    使用GoLand运行写好的代码报错,报错信息: 通过命令行进入项目目录执行go build或go run就可以成功...

  • go 打印输出

  • GOPROXY设置国内代理

    Go 1.13 及以上(推荐) 打开你的终端并执行 macOS 或 Linux 打开你的终端并执行 或者 完成。 ...

  • beego安装

    或者设置代理试试,打开你的终端并执行(Go 1.13 及以上) go env -w GO111MODULE=ong...

  • 2019-05-23 Go语言学习四 并发

    一、Go程 Go 程(goroutine)是由 Go 运行时管理的轻量级线程。 会启动一个新的 Go 程并执行 f...

网友评论

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

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