lxstvayne
@lxstvayne
Люблю Python

Как считывать из бесконечного Stdout?

Есть процесс, который работает пока не остановят, необходимо считать поток вывода до момента пока в нём что-то есть. Проблема в том что ридер зависает, ожидая следующую строчку, необходимо чтобы он не зависал, а просто переставал читать.
Привёл код, где ридер бесконечно читает из stdout. Много гуглил, но ничего нужного мне не нашёл.
package main

import (
	"bufio"
	"fmt"
	"os/exec"
	"time"
)

func main() {
	fmt.Println("Started")
	child := exec.Command("python", "./test.py")

	stdout, err := child.StdoutPipe()

	if err != nil {
		return
	}

	time.Sleep(time.Second * 3)

	var text string

	reader := bufio.NewReader(stdout)

	for {
		line, _, err := reader.ReadLine()

		if err != nil {
			break
		}

		text += string(line)

	}

	fmt.Println(text)
}

test.py
import time
import sys

while True:
    print("hello", file=sys.stdout)
    time.sleep(0.1)
  • Вопрос задан
  • 231 просмотр
Пригласить эксперта
Ответы на вопрос 4
mayton2019
@mayton2019
Bigdata Engineer
Читай не по строкам а по символам.
Ответ написан
2ord
@2ord
Используй io.Pipe, см. примеры.

Добавлю собственные демки:
buf-scan-lines - variant1
ticker-demo
И ещё

На всякий случай добавлю, что есть лучшие способы межпроцессного взаимодействия Python <-> Go, чем обработка стандартного вывода. Взять тот же ZeroMQ, в качестве примера.

Добавлено:
Простой пример с захватом StdOut: capture-stdout (проще перечисленных выше)
Ответ написан
Комментировать
Vindicar
@Vindicar
RTFM!
Проблема в том что ридер зависает, ожидая следующую строчку

Ридер не знает, это файл закончился, или же просто операция чтения длинная.
С Go не работал, но очень часто сигналом "конец файла" выступает успешно прочитанная пустая строка. Т.е. и line и err будут пустыми. Ты такой случай не проверяешь. Почитай доки на ввод-вывод в Go, как сигнализируется конец файла?

Ну и собственно, питоновский скрипт работает вечно. Конец файла вообще наступит?
Ответ написан
Комментировать
@falconandy
1. Т.к. питоновский скрипт работает вечно, я думаю, вам надо в отдельной горутине выдавать прочитанные строки в канал.
2. Вы забыли запустить сам питоновский скрипт.
3. Питон по умолчанию буферизирует вывод, можно добавить флаг -u для отключения буферизации.

Что-то типа:
package main

import (
	"bufio"
	"os/exec"
)

func main() {
	lines := read()
	for line := range lines {
		print(line)
	}
}

func read() <-chan string {
	lines := make(chan string)

	go func() {
		defer close(lines)

		child := exec.Command("python", "-u", "./test.py")

		stdout, err := child.StdoutPipe()
		if err != nil {
			return
		}

		err = child.Start()
		if err != nil {
			return
		}
		println("Started")

		reader := bufio.NewReader(stdout)

		for {
			line, err := reader.ReadString('\n')
			if err != nil {
				break
			}

			lines <- line
		}

		err = child.Wait()
		if err != nil {
			println(err)
		}
	}()

	return lines
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы