我需要从go执行子命令并分别处理stdout和stderr,同时保持stdin / stdout的输出顺序。我尝试了几种不同的方式,但无法达到正确的输出顺序;以下代码显示ouput处理顺序绝对是随机的:
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。
答案 0 :(得分:1)
没有"顺序"使用您正在执行的命令。它们是并行管道,因此它们有效地并发,就像两个goroutine并发一样。您当然可以按照收到它们的顺序存储它们,并使用通道或互斥锁标记它们的来源。为了使合成示例的输出不随机,您需要添加一些暂停。我已经成功地使用了这个方法和实际命令:
package main
import (
"fmt"
"log"
"os/exec"
"sync"
)
var (
result = ""
)
type write struct {
source string
data string
}
type writer struct {
source string
mu *sync.Mutex
writes *[]write
}
func (w *writer) Write(bytes []byte) (int, error) {
w.mu.Lock()
defer w.mu.Unlock()
*w.writes = append(*w.writes, write{
source: w.source,
data: string(bytes),
})
return len(bytes), nil
}
func main() {
cmd := exec.Command("bash", "-c", "echo TEST1; sleep .1; echo TEST2 1>&2; sleep .1; echo TEST3")
var mu sync.Mutex
var writes []write
cmd.Stderr = &writer{
source: "STDERR",
mu: &mu,
writes: &writes,
}
cmd.Stdout = &writer{
source: "STDOUT",
mu: &mu,
writes: &writes,
}
err := cmd.Start()
if err != nil {
log.Fatal(err)
}
err = cmd.Wait()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%q\n", writes)
}
将产生
[{"STDOUT" "TEST1\n"} {"STDERR" "TEST2\n"} {"STDOUT" "TEST3\n"}]