在Golang中运行外部Python代码,捕获连续的exec.Command标准输出。

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

Running external python in Golang, Catching continuous exec.Command Stdout

问题

你可以使用管道(Pipe)来实现并发地捕获Python脚本的输出并打印到Go语言的终端上。以下是修改后的代码示例:

  1. cmd := exec.Command("python", "game.py")
  2. // 创建管道
  3. stdout, err := cmd.StdoutPipe()
  4. if err != nil {
  5. panic(err)
  6. }
  7. // 启动Python脚本
  8. err = cmd.Start()
  9. if err != nil {
  10. panic(err)
  11. }
  12. // 并发地读取并打印Python脚本的输出
  13. go func() {
  14. scanner := bufio.NewScanner(stdout)
  15. for scanner.Scan() {
  16. fmt.Println(scanner.Text())
  17. }
  18. }()
  19. // 等待Python脚本执行完毕
  20. err = cmd.Wait()
  21. if err != nil {
  22. panic(err)
  23. }

这样,你就可以同时运行Python脚本并实时捕获其输出,然后在Go语言的终端上打印出来。

英文:

So my go script will call an external python like this

  1. cmd = exec.Command("python","game.py")
  2. cmd.Stdout = os.Stdout
  3. cmd.Stderr = os.Stderr
  4. go func(){
  5. err := cmd.Run()
  6. if err != nil{
  7. panic(err)
  8. }
  9. }()

It runs my python script concurrently which is awesome.
But now the problem is, my python script will run infinitely and it will print out some information from time to time. I want to "catch" these Stdout and print them out on my golang terminal. How do I do it concurrently (without waiting my python script to exit)?

答案1

得分: 7

使用cmd.Start()cmd.Wait()代替cmd.Run()

https://golang.org/pkg/os/exec/#Cmd.Run

Run启动指定的命令并等待其完成。

Start启动指定的命令,但不等待其完成。

Wait等待命令退出。它必须由Start启动。

如果你想同时捕获stdout/stderr,可以使用cmd.StdoutPipe() / cmd.StderrPipe(),并通过bufio.NewScanner()读取。

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os/exec"
  7. )
  8. func main() {
  9. cmd := exec.Command("python", "game.py")
  10. stdout, err := cmd.StdoutPipe()
  11. if err != nil {
  12. panic(err)
  13. }
  14. stderr, err := cmd.StderrPipe()
  15. if err != nil {
  16. panic(err)
  17. }
  18. err = cmd.Start()
  19. if err != nil {
  20. panic(err)
  21. }
  22. go copyOutput(stdout)
  23. go copyOutput(stderr)
  24. cmd.Wait()
  25. }
  26. func copyOutput(r io.Reader) {
  27. scanner := bufio.NewScanner(r)
  28. for scanner.Scan() {
  29. fmt.Println(scanner.Text())
  30. }
  31. }

以下是一个用于重现实时输出的示例Python代码。stdout在Python中可能会被缓冲。可能需要显式刷新。

  1. import time
  2. import sys
  3. while True:
  4. print("Hello")
  5. sys.stdout.flush()
  6. time.sleep(1)
英文:

Use cmd.Start() and cmd.Wait() instead of cmd.Run().

https://golang.org/pkg/os/exec/#Cmd.Run

> Run starts the specified command and waits for it to complete.
>
> Start starts the specified command but does not wait for it to complete.
>
> Wait waits for the command to exit. It must have been started by Start.

And if you want to capture stdout/stderr concurrently, use cmd.StdoutPipe() / cmd.StderrPipe() and read it by bufio.NewScanner()

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os/exec"
  7. )
  8. func main() {
  9. cmd := exec.Command("python", "game.py")
  10. stdout, err := cmd.StdoutPipe()
  11. if err != nil {
  12. panic(err)
  13. }
  14. stderr, err := cmd.StderrPipe()
  15. if err != nil {
  16. panic(err)
  17. }
  18. err = cmd.Start()
  19. if err != nil {
  20. panic(err)
  21. }
  22. go copyOutput(stdout)
  23. go copyOutput(stderr)
  24. cmd.Wait()
  25. }
  26. func copyOutput(r io.Reader) {
  27. scanner := bufio.NewScanner(r)
  28. for scanner.Scan() {
  29. fmt.Println(scanner.Text())
  30. }
  31. }

The following is a sample python code for reproducing real-time output. The stdout may be buffered in Python. Explicit flush may be required.

  1. import time
  2. import sys
  3. while True:
  4. print "Hello"
  5. sys.stdout.flush()
  6. time.sleep(1)

答案2

得分: 1

如果你想同时捕获标准输出和标准错误输出,确实需要使用cmd.StdoutPipe()cmd.StderrPipe()cmd.Start()cmd.Wait()

但正如文档中提到的(https://pkg.go.dev/os/exec#Cmd.StdoutPipe):

> 因此,在所有从管道中读取完成之前调用Wait是不正确的。

在调用cmd.Wait()之前,你需要等待你的goroutine完成(参见以下线程:https://github.com/golang/go/issues/38268)。

英文:

If you want to capture stdout and stderr concurrently, indeed you need to use cmd.StdoutPipe(), cmd.StderrPipe(), cmd.Start() and cmd.Wait().

But as mentioned in the documentation (https://pkg.go.dev/os/exec#Cmd.StdoutPipe):

> It is thus incorrect to call Wait before all reads from the pipe have completed.

You'll need to wait for you goroutines to finish before calling cmd.Wait() (see the following thread: https://github.com/golang/go/issues/38268)

答案3

得分: 0

你可以使用添加标志"-u"来关闭缓冲:

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

https://docs.python.org/3/using/cmdline.html#cmdoption-u

https://bugs.python.org/issue526382

英文:

You can use add the flag -u to turn off buffering,

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

https://docs.python.org/3/using/cmdline.html#cmdoption-u

https://bugs.python.org/issue526382

huangapple
  • 本文由 发表于 2017年1月1日 20:09:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/41415337.html
匿名

发表评论

匿名网友

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

确定