从`exec.Cmd`实时获取输出

huangapple go评论90阅读模式
英文:

Getting output from `exec.Cmd` in "real-time"

问题

这个问题类似于Golang - 将Exec输出复制到日志,只是它涉及到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

当我运行这个程序时,它会挂起并且没有输出任何内容,但是如果我使用os.Stdout而不是out,那么在挂起之前它会输出"hello"。为什么这两个io.Writer之间存在差异,以及如何修复它?

一些更多的诊断信息:

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

This question is similar to Golang - Copy Exec output to Log except it is concerned with the buffering of output from exec commands.

I have the following test program:

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, which the above refers to, simply contains:

print "hello"
while True:
    pass

The go program hangs when I run it and doesn't output anything, but if I use os.Stdout instead of out then it outputs "hello" before it hangs. Why is there a discrepancy between the two io.Writers and how can it be fixed?

Some more diagnostic information:

  • When the loop is removed from inf_loop.py then "hello" is output from both programs, as expected.
  • When using yes as the program instead of the python script and outputting len(p) in outstream.Write then there is output, and the output is usually 16384 or 32768. This indicates to me that this is a buffering issue, as I originally anticipated, but I still don't understand why the outstream structure is being blocked by buffering but os.Stdout isn't. One possibility is that the behaviour is the result of the way that exec passes the io.Writer directly to os.StartProcess if it is an os.File (see source for details), otherwise it creates an os.Pipe() between the process and the io.Writer, and this pipe may be causing the buffering. However, the operation and possible buffering of os.Pipe() is too low-level for me to investigate.

答案1

得分: 4

Python默认会对stdout进行缓冲。尝试运行以下程序:

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

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

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

注意-u标志。

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

英文:

Python buffers stdout by default. Try this program:

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

or run Python with unbuffered stdout and stderr:

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

Note the -u flag.

You see different results when using cmd.Stout = os.Stdout because Python uses line buffering when stdout is a terminal.

huangapple
  • 本文由 发表于 2015年6月24日 08:38:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/31015754.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定