Попен не дает вывод сразу, когда доступно - PullRequest
2 голосов
/ 01 апреля 2012

Я пытаюсь прочитать как stdout, так и stderr из 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 ]

4 голосов
/ 01 апреля 2012

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

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

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

0 голосов
/ 01 апреля 2012

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

...