Golang - 将Exec输出复制到Log

时间:2014-08-07 19:48:04

标签: go

我想及时将流程的输出重定向到log。如果我等待这个过程完成,我可以做到这一点:

cmd := exec.Command("yes", "Go is awesome") // Prints "Go is awesome", forever 
out, err := cmd.CombinedOutput()
log.Printf("%s", out)

但是,如果该过程需要很长时间或者没有完成,则这不太有用。我知道我可以像这样实时写入stdout:

cmd := exec.Command("yes")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Run()

但这并不能帮助我,因为我正在编写一个不写入终端的服务。我正在寻找能让我做类似事情的事情:

cmd := exec.Command("yes")
cmd.Stdout = log.Stdout
cmd.Stderr = log.Stdout
cmd.Run()

log无法直接访问其作者,因此无法做到这一点。当然我不是唯一遇到这个问题的人,这通常是怎么做的?

2 个答案:

答案 0 :(得分:26)

您应在此处使用pipe,例如:

stdout, err := cmd.StdoutPipe()
if err != nil {
    return 0, err
}

// start the command after having set up the pipe
if err := cmd.Start(); err != nil {
    return 0, err
}

// read command's stdout line by line
in := bufio.NewScanner(stdout)

for in.Scan() {
    log.Printf(in.Text()) // write each line to your log, or anything you need
}
if err := in.Err(); err != nil {
    log.Printf("error: %s", err)
}

我这里只处理了Stdout,但可以同时处理Stderr,例如使用goroutine。

答案 1 :(得分:3)

exec.Commandlog.Logger都基于io.Writer。您无法访问第二个,但您不需要,因为您没有更改它,并且您正在使用os.Stderr或者您更改了它并且在创建记录器时您有io.Writer

所以你只需要使用正确的io.Writer ...

来调整你的第二个例子

修改

经过一些尝试之后,我认为您可以通过将log.Logger嵌入到将实现io.Writer接口的结构中来实现...

示例:

type LogWriter struct {
    logger *log.Logger
}

func NewLogWriter(l *log.Logger) *LogWriter {
    lw := &LogWriter{}
    lw.logger = l
    return lw
}

func (lw LogWriter) Write (p []byte) (n int, err error) {
    lw.logger.Println(p)
    return len(p), nil
}

然后将其传递给您的exec.Command输出...

cmd := exec.Command(…)
cmd.Stdout = NewLogWriter(log.New(…))

您可以将它用于标准输出或错误输出,或为每个输出创建新对象。

<强> EDIT2

由于我将在下一个项目中使用此技巧,我将其放入github的包中。随意提供反馈等。