dongqian5384
dongqian5384
2015-06-28 02:52

将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 dongxin9759 6年前

    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 doutuan8887 5年前

    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
    }
    
    点赞 评论 复制链接分享

相关推荐