dongqian5384
2015-06-28 02:52
浏览 286
已采纳

将cmd stdout和stderr作为字符串返回,而不是在golang中打印到控制台

I am executing bash commands from a golang application. Now the stdout and stderr go directly to console:

cmd.Stdout = os.Stdout 
cmd.Stderr = os.Stderr

But I would like stdout and stderr to be returned as string variables from the runBashCommandAndKillIfTooSlow function without printing to the console immediately. How to implement this?

The code:

package main

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

func main() {
    ok, outString, errString := runBashCommandAndKillIfTooSlow("ls -la", 2000)
    fmt.Println("ok")
    fmt.Println(ok)
    fmt.Println("outString")
    fmt.Println(outString)
    fmt.Println("errString")
    fmt.Println(errString)
}

/*
run bash command and kill it if it works longer than "killInMilliSeconds" milliseconds
*/
func runBashCommandAndKillIfTooSlow(command string, killInMilliSeconds time.Duration) (okResult bool, stdout, stderr string) {
    fmt.Println("running bash command...")
    fmt.Println(command)
    cmd := exec.Command("sh", "-c", command)

    cmd.Stdout = os.Stdout // cmd.Stdout -> stdout
    cmd.Stderr = os.Stderr // cmd.Stderr -> stderr

    okResult = true

    err := cmd.Start()
    log.Printf("Waiting for command to finish...")
    done := make(chan error, 1)
    go func() {
        done <- cmd.Wait()
    }()
    select {
    case <-time.After(killInMilliSeconds * time.Millisecond):
        if err := cmd.Process.Kill(); err != nil {
            log.Fatal("failed to kill: ", err)
            okResult = false
        }
        <-done // allow goroutine to exit
        // log.Println("process killed")
    case err := <-done:

        if err != nil {
            log.Printf("process done with error = %v", err)
            okResult = false
        }
    }
    if err != nil {
        log.Fatal(err)
        okResult = false
    }
    return
}

By the way, the program should keep its ability to kill the bash command if it was too slow (killInMilliSeconds parameter).

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • dongxin9759 2015-06-28 03:37
    已采纳

    Set the output to a bytes.Buffer:

    var outbuf, errbuf bytes.Buffer
    cmd.Stdout = &outbuf
    cmd.Stderr = &errbuf
    

    After running the command, you can get the stdout and stderr as a string by calling the Buffer.String() method:

    stdout := outbuf.String()
    stderr := errbuf.String()
    
    打赏 评论
  • doutuan8887 2016-10-10 23:22

    You can simplify this quite a bit by using cmd.Run() instead of cmd.Start() to have it automatically wait for it to be finish, and use exec.CommandContext() to have it timeout. This will also output in the correct order, whereas the original program is out of order due to go routines.

    Here's the exact same program simplified and using @Mello Marmot's answer:

    package main
    
    import (
        "bytes"
        "fmt"
        "log"
        "os"
        "os/exec"
        "time"
    
        "golang.org/x/net/context"
    )
    
    func main() {
        ctx := context.Background()
        ok, outString, errString := runBashCommandAndKillIfTooSlow(ctx, "ls -la", 2000*time.Millisecond)
        fmt.Println("ok")
        fmt.Println(ok)
        fmt.Println("outString")
        fmt.Println(outString)
        fmt.Println("errString")
        fmt.Println(errString)
    }
    
    /*
    run bash command and kill it if it works longer than "killIn"
    */
    func runBashCommandAndKillIfTooSlow(ctx context.Context, command string, killIn time.Duration) (okResult bool, stdout, stderr string) {
        fmt.Println("running bash command...")
        fmt.Println(command)
        ctx, _ = context.WithTimeout(ctx, killIn)
        cmd := exec.CommandContext(ctx, "sh", "-c", command)
    
        // Set output to Byte Buffers
        var outb, errb bytes.Buffer
        cmd.Stdout = &outb
        cmd.Stderr = &errb
    
        okResult = true
        err := cmd.Run()
        stdout = outb.String()
        stderr = errb.String()
        if err != nil {
            log.Fatal(err)
            okResult = false
        }
        return
    }
    
    打赏 评论

相关推荐 更多相似问题