【发布时间】:2015-08-19 09:57:54
【问题描述】:
我需要从 go 执行子命令并分别处理它的 stdout 和 stderr,同时保持 stdin/stdout 的输出顺序。我尝试了几种不同的方法,但无法实现正确的输出顺序;以下代码显示输出处理顺序是绝对随机的:
package main
import (
"fmt"
"log"
"os/exec"
)
var (
result = ""
)
type writer struct {
result string
write func(bytes []byte)
}
func (writer *writer) Write(bytes []byte) (int, error) {
writer.result += string(bytes) // process result later
result += string(bytes)
return len(bytes), nil
}
func main() {
cmd := exec.Command("bash", "-c", "echo TEST1; echo TEST2 1>&2; echo TEST3")
stderr := &writer{}
cmd.Stderr = stderr
stdout := &writer{}
cmd.Stdout = stdout
err := cmd.Start()
if err != nil {
log.Fatal(err)
}
err = cmd.Wait()
if err != nil {
log.Fatal(err)
}
fmt.Println(result)
}
多次运行代码可以输出如下:
$ go run main.go
TEST1
TEST3
TEST2
我希望在所有情况下都有以下结果:
$ go run main.go
TEST1
TEST2
TEST3
我无法调用 cmd.CombinedOutput,因为我需要分别实时处理 stdout/stderr。
【问题讨论】:
-
嗯.. 出于某种原因,我无法重现您的问题。我总是得到 TEST1 TEST2 TEST3
-
@bshuster13 我可以在 Ubuntu 14.04 上重现它。
-
一般你不能,因为许多操作系统会缓冲标准输出(特别是如果它没有连接到终端)但不缓冲标准错误。我不知道一种与操作系统无关的方式来更改缓冲(例如,FreeBSD 有
stdbuf(1))。 -
我尝试在 Arch Linux 上添加带有
stdbuf -o 0 -e 0的命令并得到相同的结果。有没有办法告诉操作系统不要缓冲结果或模拟终端行为?操作系统特定的方法可以做到这一点。