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 ответа:
Только stderr не имеет буфера, а не stdout. То, что вы хотите, не может быть сделано с помощью встроенных оболочек в одиночку. Поведение буферизации определяется в библиотеке stdio (3) C, которая применяет линейную буферизацию только в том случае, когда вывод осуществляется на терминал. Когда выходные данные поступают в канал, они буферизуются по каналу, а не по линии, и поэтому данные не передаются в ядро, а оттуда на другой конец канала, пока буфер канала не заполнится.
Кроме того, оболочка не имеет доступа к libc буферные управляющие функции, такие какsetbuf (3) и friends. Единственное возможное решение в оболочке-запустить совместный процесс на псевдо-tty, а управление pty-сложная тема. Гораздо проще переписать эквивалентный сценарий оболочки на языке, который предоставляет доступ к низкоуровневым функциям буферизации для выходных потоков, чем организовать запуск чего-то через pty.
Однако, если вы вызовете
/bin/echo
вместо встроенной оболочкиecho
, Вы можете найти его более подходящим для вашего симпатия. Это работает, потому что теперь вся строка сбрасывается, когда вновь запущенный процесс/bin/echo
завершается каждый раз. Это вряд ли эффективное использование системных ресурсов, но может быть эффективным использованием ваших собственных.