python popen, stdout показывает в strace, но не в popen.stdout.read () - PullRequest
0 голосов
/ 19 декабря 2018

У меня есть приведенный ниже скрипт на python, который я использую, чтобы попытаться прочитать программу, в которой нет нужных символов новой строки для всего.Это позволяет выполнять чтение, не беспокоясь о блокировке операций чтения.Однако, поскольку я не знаю достаточно о потоке, я подозреваю, что именно в этом заключается моя проблема.

import subprocess
import shlex
import os
import time
from threading import Thread
import queue


class NonBlockingStreamReader:
    def __init__(self, stream):
        """
        :param stream: the stream to read from.  Usually stdout or stderr.
        """
        self._s = stream
        self._q = queue.Queue()

        def _populate_queue(_stream, _queue):
            """Collect lines from 'stream' and put them in 'queue'"""
            while True:
                _char = _stream.read(1)
                if _char:
                    _queue.put(_char)
                else:
                    raise UnexpectedEndOfStream

        self._t = Thread(
            target=_populate_queue,
            args=(
                self._s,
                self._q
            )
        )
        self._t.daemon = True
        self._t.start() # Start collecting characters from the stream

    def readchar(self, timeout=None):
        try:
            _tmp = self._q.get(block=timeout is not None, timeout=timeout)
            return _tmp
        except queue.Empty:
            return None


class UnexpectedEndOfStream(Exception):
    pass


def main():
    proc = subprocess.Popen(
        shlex.split('strace -o /home/arts/dlm/trace_output.txt stdbuf -o0 /home/arts/dlm/test'),
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )

    nbsr = NonBlockingStreamReader(proc.stdout)

    _data = b''
    while True:
        _char = nbsr.readchar(0.1)
        if not _char:
            break
        else:
            _data += _char
    print(_data.decode())

    proc.stdin.write(b'12345\n')

    _data = b''
    while True:
        _char = nbsr.readchar(5)
        if not _char:
            break
        else:
            _data += _char
    print(_data.decode())
    print('Annnnd done.')


if __name__ == '__main__':
    main()

Вот ожидаемый результат работы программы test:

Line 1 test
Line 2 test
Line 3 input: 12345      <--- input from user
Line 4 test: 12345

Вотвывод strace:

write(1, "Line 1 test", 11)             = 11
write(1, "\n", 1)                       = 1
write(1, "Line 2 test", 11)             = 11
write(1, "\n", 1)                       = 1
write(1, "Line 3 input: ", 14)          = 14
fstat(0, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb76716d000
read(0, "12345\n", 4096)                = 6
write(1, "Line 4 test: 12345\n\n", 20)  = 20
exit_group(1)                           = ?

Это показывает (по крайней мере мне), что приложение предоставляет запрошенный вывод.read(0, "12345\n", 4096) показывает proc.stdin.write(b'12345\n'), если я что-то упустил.Следующая строка показывает, что он читает ожидаемый результат.Тем не менее, РЕАЛЬНЫЕ выходные данные:

Line 1 test
Line 2 test
Line 3 input:

Annnnd done.

Если я поставлю некоторые операторы печати после _char = _stream.read(1), он ничего не показывает.Если я добавлю их в функцию readchar, то она покажет None.

Что-то ломает стандартный вывод, поэтому, куда бы он ни шел, он не идет в трубу.Кто-нибудь может направить меня в правильном направлении?

Ответы [ 2 ]

0 голосов
/ 19 декабря 2018

Две проблемы с вашим вторым циклом чтения:

  • Он разрывается при пустом _data вместо _char, а _data установлен на b'' в верхней части цикла,поэтому он всегда будет выходить, ничего не записывая в _data.
  • Он прерывается, как только буфер читателя пуст, что может быть или не быть после того, как весь вывод дочернего процесса был записан или прочитан,в зависимости от времени вовлеченных процессов и потоков.

Вы, вероятно, захотите установить флаг EOF в считывателе вместо того, чтобы поднимать UnexpectedEndOfStream, а затем основывать свой цикл на этом (или, скорее,на основе производного условия, основанного на установленном флаге и пустой очереди).

0 голосов
/ 19 декабря 2018

Разобрался.Требуется bufsize=0 в качестве аргумента для Popen.

...