【问题标题】:Getting output from `exec.Cmd` in "real-time"“实时”从 `exec.Cmd` 获取输出
【发布时间】:2015-06-24 00:38:35
【问题描述】:

这个问题与Golang - Copy Exec output to Log 类似,只是它与exec 命令的输出缓冲有关。

我有以下测试程序:

package main

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

func main() {
    cmd := exec.Command("python", "inf_loop.py")
    var out outstream
    cmd.Stdout = out
    if err := cmd.Start(); err != nil {
        log.Fatal(err)
    }
    fmt.Println(cmd.Wait())
}

type outstream struct{}

func (out outstream) Write(p []byte) (int, error) {
    fmt.Println(string(p))
    return len(p), nil
}

inf_loop.py,上面提到的,只是包含:

print "hello"
while True:
    pass

go 程序在我运行时挂起并且不输出任何内容,但如果我使用os.Stdout 而不是out,那么它会在挂起之前输出“hello”。为什么两个io.Writers 之间存在差异,如何解决?

更多诊断信息:

  • 当循环从inf_loop.py 中删除时,两个程序都会输出“hello”,正如预期的那样。
  • 当使用yes作为程序而不是python脚本并在outstream.Write中输出len(p)然后有输出,输出通常是16384或32768。这向我表明这是一个缓冲问题,正如我最初预期的那样,但我仍然不明白为什么 outstream 结构被缓冲阻塞但 os.Stdout 不是。一种可能性是该行为是execio.Writer 直接传递给os.StartProcess(如果它是os.File)的结果(有关详细信息,请参阅source),否则它会在两者之间创建os.Pipe()进程和io.Writer,这个管道可能会导致缓冲。但是,os.Pipe() 的操作和可能的缓冲级别太低,我无法调查。

【问题讨论】:

    标签: go exec stdout buffering


    【解决方案1】:

    Python 默认缓冲标准输出。试试这个程序:

    import sys
    print "hello"
    sys.stdout.flush()
    while True:
        pass
    

    或使用无缓冲的 stdout 和 stderr 运行 Python:

    cmd := exec.Command("python", "-u", "foo.py")
    

    注意 -u 标志。

    使用cmd.Stout = os.Stdout 时会看到不同的结果,因为当 stdout 是终端时 Python 使用行缓冲。

    【讨论】:

    • 完美答案,谢谢!只是为了将来参考,我很好奇Python如何知道stdout是后一种情况下的终端,发现一般的方法是使用ioctl
    • 顺便说一句,Linux(实际上是 GNU coreutils)和 FreeBSD(可能还有其他)有stdbuf,它改变了任意命令的初始缓冲。对于没有选项的命令(例如此处显示的 -u 或 grep 的 --line-buffered)直接执行此操作很有用。
    猜你喜欢
    • 1970-01-01
    • 2012-02-07
    • 1970-01-01
    • 2021-12-27
    • 2013-10-30
    • 2021-04-15
    • 2015-06-08
    • 2013-10-05
    相关资源
    最近更新 更多