我想使用os/exec
软件包在Go中管理一个流程。我想启动它,并且能够读取输出并多次写入输入。
我在下面的代码menu.py
中启动的过程只是一个Python脚本,它对输入内容进行回显。
func ReadOutput(rc io.ReadCloser) (string, error) {
x, err := ioutil.ReadAll(rc)
s := string(x)
return s, err
}
func main() {
cmd := exec.Command("python", "menu.py")
stdout, err := cmd.StdoutPipe()
Check(err)
stdin, err := cmd.StdinPipe()
Check(err)
err = cmd.Start()
Check(err)
go func() {
defer stdin.Close() // If I don't close the stdin pipe, the python code will never take what I write in it
io.WriteString(stdin, "blub")
}()
s, err := ReadOutput(stdout)
if err != nil {
Log("Process is finished ..")
}
Log(s)
// STDIN IS CLOSED, I CAN'T RETRY !
}
还有menu.py
的简单代码:
while 1 == 1:
name = raw_input("")
print "Hello, %s. \n" % name
Go代码可以工作,但是如果我在写完之后不关闭stdin管道,则python代码将永远不会占用其中的内容。如果我想按时只发送一件事是可以的,但是几秒钟后又想发送什么呢?管道已关闭!我应该怎么做?问题可能是“如何从WriteCloser接口冲洗管道?”我想
答案 0 :(得分:1)
我认为这里的主要问题是python进程无法按您期望的方式工作。这是一个执行相同操作的bash脚本echo.sh
:
#!/bin/bash
while read INPUT
do echo "Hello, $INPUT."
done
从代码的修改版本中调用此脚本不会出现与需要关闭stdin
相同的问题:
func ReadOutput(output chan string, rc io.ReadCloser) {
r := bufio.NewReader(rc)
for {
x, _ := r.ReadString('\n')
output <- string(x)
}
}
func main() {
cmd := exec.Command("bash", "echo.sh")
stdout, err := cmd.StdoutPipe()
Check(err)
stdin, err := cmd.StdinPipe()
Check(err)
err = cmd.Start()
Check(err)
go func() {
io.WriteString(stdin, "blab\n")
io.WriteString(stdin, "blob\n")
io.WriteString(stdin, "booo\n")
}()
output := make(chan string)
defer close(output)
go ReadOutput(output, stdout)
for o := range output {
Log(o)
}
}
Go代码需要进行一些小的更改-ReadOutput
方法需要进行修改才能不被阻塞-ioutil.ReadAll
在返回之前将等待EOF
。
深入研究,似乎真正的问题是raw_input
的行为-它没有按预期刷新标准输出。您可以将-u
标志传递给python以获得所需的行为:
cmd := exec.Command("python", "-u", "menu.py")
或更新您的python代码以使用sys.stdin.readline()
代替raw_input()
(请参见相关的错误报告:https://bugs.python.org/issue526382)。
答案 1 :(得分:0)
即使您的python脚本存在一些问题。主要问题是golang管道。解决此问题的技巧是使用两个管道,如下所示:
// parentprocess.go
package main
import (
"bufio"
"log"
"io"
"os/exec"
)
func request(r *bufio.Reader, w io.Writer, str string) string {
w.Write([]byte(str))
w.Write([]byte("\n"))
str, err := r.ReadString('\n')
if err != nil {
panic(err)
}
return str[:len(str)-1]
}
func main() {
cmd := exec.Command("bash", "menu.sh")
inr, inw := io.Pipe()
outr, outw := io.Pipe()
cmd.Stdin = inr
cmd.Stdout = outw
if err := cmd.Start(); err != nil {
panic(err)
}
go cmd.Wait()
reader := bufio.NewReader(outr)
log.Printf(request(reader, inw, "Tom"))
log.Printf(request(reader, inw, "Rose"))
}
子流程代码与python代码具有相同的逻辑,如下所示:
#!/usr/bin/env bash
# menu.sh
while true; do
read -r name
echo "Hello, $name."
done
如果要使用python代码,则应进行一些更改:
while 1 == 1:
try:
name = raw_input("")
print "Hello, %s. \n" % name
sys.stdout.flush() # there's a stdout buffer
except:
pass # make sure this process won't die when come across 'EOF'
答案 2 :(得分:-1)
// StdinPipe returns a pipe that will be connected to the command's
// standard input when the command starts.
// The pipe will be closed automatically after Wait sees the command exit.
// A caller need only call Close to force the pipe to close sooner.
// For example, if the command being run will not exit until standard input`enter code here`
// is closed, the caller must close the pipe.
func (c *Cmd) StdinPipe() (io.WriteCloser, error) {}