В python, как проверить стандартный вывод из объекта subprocess.Popen, чтобы что-нибудь прочитать? - PullRequest
3 голосов
/ 08 августа 2011

В python, как проверить вывод из объекта subprocess.Popen, чтобы что-нибудь прочитать?Я пишу обертку вокруг инструмента, который иногда работает часами подряд.Использование .readline () в stdout дочернего процесса сильно снижает скорость работы скрипта, если он выполняется дольше, чем несколько минут.Мне нужен способ более эффективно проверить стандартный вывод, если есть что почитать.Кстати, этот конкретный инструмент пишет только полные строки одновременно.Сценарий выглядит так:

    #!/usr/bin/python -u
    #thiswrap.py

    import sys, time
    from subprocess import *

    chldp = Popen(sys.argv[1], bufsize=0, stdout=PIPE, close_fds=True)
    chstdin,chstdout=chldp.stdin,chldp.stdout
    startnoti=False

    while not chldp.poll():
        rrl=chstdout.readline() # <--- this is where the problem is
        if rrl[-8:]=='REDACTED TEXT':
            sys.stdout.write(rrl[:-1]+'   \r')
            if not startnoti: startnoti=True
        else:
            if startnoti: sys.stdout.write('\n')
            sys.stdout.write(rrl)
            if startnoti: # REDACTED
            time.sleep(0.1)
        time.sleep(0.1)

Есть идеи?

Ответы [ 2 ]

4 голосов
/ 08 августа 2011

Вам нужно установить дескрипторы файлов как неблокирующие, вы можете сделать это, используя fcntl :

import sys, time, fcntl, os
from subprocess import *

chldp = Popen(sys.argv[1], bufsize=0, stdout=PIPE, close_fds=True)
chstdin, chstdout = chldp.stdin, chldp.stdout
fl = fcntl.fcntl(chstdout, fcntl.F_GETFL)
fcntl.fcntl(chstdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)

while chldp.poll() is not None:
    try:
        rrl = chstdout.readline()
    except IOError:
        time.sleep(0.1)
        continue
    # use rrl

Когда нет доступных данных, IOError будетподнят на readline().

Обратите внимание, что, поскольку chldp.poll() может вернуть 0 по завершении подпроцесса, вам, вероятно, следует использовать childp.poll() is not None в вашем while вместо not childp.poll().

1 голос
/ 06 февраля 2012

К сожалению, не существует готового способа опроса условия "в канале достаточно данных с разрывом строки, чтобы readline () немедленно вернулась".

Если вы хотите строку за раз и не хотите блокировать ее, вы также можете:

Либо реализуйте свою собственную буферизацию через класс или генератор и выполните опрос, например ::

def linereader():
    data = ""
    while True:
        if poll(f.fd):
            data += f.read(100)
        lines = data.split("\n")
        data = lines[-1]
        for line in lines[:-1]:
            yield line

# use
for line in linereader():
    if line:
       print line
    else:
       time.sleep(...)

Или используйте потоки (оставьте читателю в качестве упражнения, обратите внимание, что в старых версиях Python есть ошибка, если вы запускаете подпроцесс из потока, отличного от основного)

...