英文:
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
却没有。一个可能性是,这种行为是由于exec
将io.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.Writer
s 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 outputtinglen(p)
inoutstream.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 theoutstream
structure is being blocked by buffering butos.Stdout
isn't. One possibility is that the behaviour is the result of the way thatexec
passes theio.Writer
directly toos.StartProcess
if it is anos.File
(see source for details), otherwise it creates anos.Pipe()
between the process and theio.Writer
, and this pipe may be causing the buffering. However, the operation and possible buffering ofos.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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论