Popen не дает выход сразу, когда он доступен


Я пытаюсь читать из обоих stdout и stderr из Popen и распечатать их. Команда, которую я выполняю с Popen, следующая

#!/bin/bash

i=10
while (( i > 0 )); do
    sleep 1s
    echo heyo-$i
    i="$((i-1))"
done

echo 'to error' >&2
Когда я запускаю это в оболочке, я получаю одну строку вывода, а затем второй разрыв, а затем еще одну строку и т. д. Однако я не могу воссоздать это с помощью python. Я запускаю два потока, по одному для чтения из stdout и stderr, помещаю прочитанные строки в Queue и другой поток, который берет элементы из этой очереди и распечатывает их. Но при этом я вижу, что все выходные данные распечатываются сразу же, после завершения подпроцесса. Я хочу, чтобы строки печатались как и когда они будут echo'ed.

Вот мой код python:

# The `randoms` is in the $PATH
proc = sp.Popen(['randoms'], stdout=sp.PIPE, stderr=sp.PIPE, bufsize=0)

q = Queue()

def stream_watcher(stream, name=None):
    """Take lines from the stream and put them in the q"""
    for line in stream:
        q.put((name, line))
    if not stream.closed:
        stream.close()

Thread(target=stream_watcher, args=(proc.stdout, 'out')).start()
Thread(target=stream_watcher, args=(proc.stderr, 'err')).start()

def displayer():
    """Take lines from the q and add them to the display"""
    while True:
        try:
            name, line = q.get(True, 1)
        except Empty:
            if proc.poll() is not None:
                break
        else:
            # Print line with the trailing newline character
            print(name.upper(), '->', line[:-1])
            q.task_done()

    print('-*- FINISHED -*-')

Thread(target=displayer).start()

Есть идеи? Чего мне здесь не хватает?

2 2

2 ответа:

Только stderr не имеет буфера, а не stdout. То, что вы хотите, не может быть сделано с помощью встроенных оболочек в одиночку. Поведение буферизации определяется в библиотеке stdio (3) C, которая применяет линейную буферизацию только в том случае, когда вывод осуществляется на терминал. Когда выходные данные поступают в канал, они буферизуются по каналу, а не по линии, и поэтому данные не передаются в ядро, а оттуда на другой конец канала, пока буфер канала не заполнится.

Кроме того, оболочка не имеет доступа к libc буферные управляющие функции, такие какsetbuf (3) и friends. Единственное возможное решение в оболочке-запустить совместный процесс на псевдо-tty, а управление pty-сложная тема. Гораздо проще переписать эквивалентный сценарий оболочки на языке, который предоставляет доступ к низкоуровневым функциям буферизации для выходных потоков, чем организовать запуск чего-то через pty.

Однако, если вы вызовете /bin/echo вместо встроенной оболочки echo, Вы можете найти его более подходящим для вашего симпатия. Это работает, потому что теперь вся строка сбрасывается, когда вновь запущенный процесс /bin/echo завершается каждый раз. Это вряд ли эффективное использование системных ресурсов, но может быть эффективным использованием ваших собственных.

IIRC, установка shell=True на Popen должна это сделать.