将stdout流写入文件

时间:2018-02-22 12:05:32

标签: file go stream stdout

我正在通过exec.Command()运行外部进程,我希望命令中的stdout能够实时打印和写入文件(类似于从命令行使用tee )。

我可以通过扫描仪和编写器实现这一目标:

cmd := exec.Command("mycmd")
cmdStdOut, _ := cmd.StdoutPipe()

s := bufio.NewScanner(cmdStdOut)
f, _ := os.Create("stdout.log")
w := bufio.NewWriter(f)

go func() {
    for s.Scan(){
        t := s.Text()
        fmt.Println(t)
        fmt.Fprint(w, t)
        w.Flush()    
    }
}

是否有更惯用的方法可以避免遭遇ScanFlush

2 个答案:

答案 0 :(得分:3)

将多重写入器分配给commmand的stdout,该stdout写入文件和管道。然后,您可以使用管道的读取结束来跟随输出。

此示例的行为类似于tee工具:

package main

import (
    "io"
    "os"
    "os/exec"
)

func main() {
    var f *os.File // e.g. os.Create, os.Open

    r, w := io.Pipe()
    defer w.Close()

    cmd := exec.Command("mycmd")
    cmd.Stdout = io.MultiWriter(w, f)

    // do something with the output while cmd is running by reading from r
    go io.Copy(os.Stdout, r) 

    cmd.Run()
}

替代StdoutPipe:

package main

import (
    "io"
    "os"
    "os/exec"
)

func main() {
    var f *os.File

    cmd := exec.Command("date")
    stdout, _ := cmd.StdoutPipe()

    go io.Copy(io.MultiWriter(f, os.Stdout), stdout)

    cmd.Run()
}

答案 1 :(得分:0)

为简洁起见,忽略错误。如其他答案所述,您可以在 io.Copy 中使用 io.MultiWriter ,但是在处理 stdout 时exec.Cmd ,您需要知道 Wait 在命令终止后立即关闭管道,如文档(https://golang.org/pkg/os/exec/#Cmd.StdoutPipe)所述。

等待将在看到命令退出后关闭管道,因此大多数调用者无需自己关闭管道。因此,在管道中的所有读取完成之前调用“等待”是不正确的。

忽略这一点可能会导致输出的某些部分无法读取,因此会丢失。而是不要使用 Run ,而要使用 Start Wait 。例如。

package main

import (
    "io"
    "os"
    "os/exec"
)

func main() {
    cmd := exec.Command("date")
    stdout, _ := cmd.StdoutPipe()
    f, _ := os.Create("stdout.log")

    cmd.Start()
    io.Copy(io.MultiWriter(f, os.Stdout), stdout)
    cmd.Wait()
}

这将确保从 stdout 中读取所有内容,然后关闭所有管道。