2009-08-15 4 views
5

Я хочу захватить stdout из long-ish запущенного процесса, запущенного через subprocess.Popen(...), поэтому я использую stdout=PIPE как arg.Python - захват Popen stdout И отображение на консоли?

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

Возможно ли это?

Cheers.

+0

Дубликат: http://stackoverflow.com/questions/803265/getting-realtime-output-using-subprocess и http://webproverka.com/ : //stackoverflow.com/questions/874815/how-do-i-get-real-time-information-back-from-a-subprocess-popen-in-python-2-5 –

+0

Связанный: [Python: чтение потоковой передачи input from subprocess.communicate()] (http://stackoverflow.com/a/17698359/4279) – jfs

ответ

1

Можете ли вы просто print его, как вы читаете его из трубы?

+0

Ах! (момент лампочки!) - p.stdout является файлом, подобным объекту. Так что мне просто нужно вызвать readline() на нем в цикле? Правильно? Каков наилучший способ выхода из цикла - когда p.stdout возвращает пустую строку? Должен ли я по-прежнему делать p.wait() после того, как это было сделано? –

+1

@Steve: пустая строка возвращается из чтения p.stdout означает, что ребенок закрыл свое выступление, но не обязательно, что он завершен. p.wait() будет ждать завершения. – RichieHindle

5

Буферизация вашего долговременного подпроцесса, вероятно, приведет к тому, что ваша консоль выйдет из строя и очень плохой UX. Я предлагаю вам вместо этого использовать pexpect (или в Windows, wexpect), чтобы победить такую ​​буферизацию и получить плавный регулярный вывод из подпроцесса. Например (на любой Unix-у системы, после установки pexpect):

>>> import pexpect 
>>> child = pexpect.spawn('/bin/bash -c "echo ba; sleep 1; echo bu"', logfile=sys.stdout); x=child.expect(pexpect.EOF); child.close() 
ba 
bu 
>>> child.before 
'ba\r\nbu\r\n' 

ба и бу будет идти с надлежащим выбором времени (около секунды между ними). Обратите внимание, что вывод не подлежит нормальной обработке терминалов, поэтому возвраты каретки остаются там - вам нужно будет постобработать строку самостоятельно (просто просто .replace! -), если вам нужно \n как конец строки маркеры (недостаток обработки важен только в том случае, если подпроцесс записывает двоичные данные в его stdout - это гарантирует, что все данные остаются нетронутыми!).

1

В качестве альтернативы вы можете подключить свой процесс к тройнику и захватить только один из потоков. Что-то вдоль линий sh -c 'process interesting stuff' | tee /dev/stderr.

Конечно, это работает только в Unix-подобных системах.

+1

К сожалению, все еще подвержены буферизации, вызванной рывкой :-(. –

2

С. Лотт комментарий указывает на Getting realtime output using subprocess и Real-time intercepting of stdout from another process in Python

Мне любопытно, что ответ Алекс здесь отличается от своего ответа 1085071. Мои простые маленькие эксперименты с ответами в двух других упомянутых вопросов, дал хорошие результаты ...

Я пошел и посмотрел на блеск в соответствии с ответом Алекса выше, но я должен сказать, что прочитав комментарии в коде, у меня не было очень хорошего чувства об использовании его.

Я предполагаю, что мета-вопрос здесь, когда будет pexpect/wexpect быть одной из включенных батарей?

1

Вдохновленный предложением pty.openpty() где-то выше, проверено на python2.6, linux. Публикация, так как это потребовалось некоторое время, чтобы сделать это работает должным образом, без буферизации ...

def call_and_peek_output(cmd, shell=False): 
    import pty, subprocess 
    master, slave = pty.openpty() 
    p = subprocess.Popen(cmd, shell=shell, stdin=None, stdout=slave, close_fds=True) 
    os.close(slave) 
    line = "" 
    while True: 
     try: 
      ch = os.read(master, 1) 
     except OSError: 
      # We get this exception when the spawn process closes all references to the 
      # pty descriptor which we passed him to use for stdout 
      # (typically when it and its childs exit) 
      break 
     line += ch 
     sys.stdout.write(ch) 
     if ch == '\n': 
      yield line 
      line = "" 
    if line: 
     yield line 

    ret = p.wait() 
    if ret: 
     raise subprocess.CalledProcessError(ret, cmd) 

for l in call_and_peek_output("ls /", shell=True): 
    pass