如何用Go的os.StartProcess实时捕获并处理进程stdout流
问题:在Windows平台用Go的
os.StartProcess实时捕获子进程stdout 问题背景
我需要在Go语言中通过os.StartProcess以流的形式获取子进程的stdout,原因是Windows平台下exec.Command无法满足需求——它在Windows中运行一段时间后会冻结,这是一个长期存在的问题。
我的实际需求是将捕获到的输出以流的形式实时发送至外部日志服务,打印仅作为示例。我曾尝试使用io pipes、MultiReader和bufio.NewScanner,但均未成功。
初始实现代码
package main import ( "bufio" "bytes" "io" "log" "os" "path/filepath" ) func main() { fileDir := "C:\\Users\\username" entrypoint := "main.py" r, w, _ := os.Pipe() var procAttr os.ProcAttr procAttr.Files = []*os.File{os.Stdin, w, os.Stderr} outC := make(chan string) go func() { var buf bytes.Buffer io.Copy(&buf, r) // 我希望能实时打印缓冲区中写入的内容 // 比如 log.Println("buf: ", buf.String()) // 但这样只会在执行结束时打印 // 因为io.copy是流式循环 outC <- buf.String() }() p, err := os.StartProcess( "C:\\Windows\\system32\\cmd.exe", []string{ "C:\\Windows\\system32\\cmd.exe", "/C", filepath.Join(filepath.Dir(fileDir), "venv", "Scripts", "python.exe"), filepath.Join(fileDir, entrypoint), }, &procAttr, ) if err != nil { log.Println(err) } else { log.Println("os.StartProcess ok") pState, _ := p.Wait() log.Println("ExitCode:", pState.ExitCode()) log.Println("Success:", pState.Success()) w.Close() r.Close() out := <-outC log.Println("Captured:", out) } }
补充说明:为何Windows下无法使用exec.Command
Go语言的Windows实现存在一个长期未解决的问题:exec.Command()内部使用os.Stat(),无法正确读取带重解析点的文件,而os.Lstat()可以做到。Windows应用商店应用使用的应用执行别名本质上就是带重解析点的零字节文件。解决方案是使用更底层的os.StartProcess()API,尽管它比os/exec包的exec.Command()更难使用。
已实现的解决方案
感谢帮助,我已经实现了预期功能,将继续测试,如有必要会更新内容:
package main import ( "bufio" "context" "log" "os" "path/filepath" ) func main() { fileDir := "C:\\Users\\username" entrypoint := "main.py" r, w, _ := os.Pipe() var procAttr os.ProcAttr procAttr.Files = []*os.File{os.Stdin, w, os.Stderr} grCtx, grCancel := context.WithCancel(context.Background()) go func(intCtx context.Context) { rs := bufio.NewScanner(r) for rs.Scan() { select { case <-intCtx.Done(): log.Println("scanner finished") return default: log.Println("scanner text: ", rs.Text()) // 此处可替换为发送至外部日志服务的逻辑 } } }(grCtx) p, err := os.StartProcess( "C:\\Windows\\system32\\cmd.exe", []string{ "C:\\Windows\\system32\\cmd.exe", "/C", filepath.Join(filepath.Dir(fileDir), "venv", "Scripts", "python.exe"), filepath.Join(fileDir, entrypoint), }, &procAttr, ) if err != nil { log.Println(err) } else { log.Println("os.StartProcess ok") pState, _ := p.Wait() log.Println("ExitCode:", pState.ExitCode()) log.Println("Success:", pState.Success()) } w.Close() r.Close() grCancel() }
内容的提问来源于stack exchange,提问作者rsvix
相关产品推荐
相关产品推荐

