Golang OS / Exec промывки стандартного ввода, не закрывая его - PullRequest
1 голос
/ 23 марта 2019

Я бы хотел управлять процессом в Go с помощью пакета os/exec. Я хотел бы запустить его и иметь возможность читать вывод и записывать несколько раз на вход.

Процесс, который я запускаю в приведенном ниже коде, 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?» Я полагаю

1 Ответ

1 голос
/ 23 марта 2019

Я думаю, что основная проблема здесь в том, что процесс 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 - он не сбрасывает stdout, как ожидалось.Вы можете передать флаг -u Python, чтобы получить желаемое поведение:

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

или обновить свой код Python, чтобы использовать sys.stdin.readline() вместо raw_input() (см. Этот связанный отчет об ошибке: https://bugs.python.org/issue526382).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...