我正在通过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()
}
}
是否有更惯用的方法可以避免遭遇Scan
和Flush
?
答案 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 中读取所有内容,然后关闭所有管道。