2013-11-06 7 views
2

Я пытаюсь напечатать stdout в реальном времени для подпроцесса, но похоже, что stdout буферизуется даже с bufsize = 0, и я не могу понять, как заставить его работать, у меня всегда есть задержка.Печать вывода в реальном времени из подпроцесса

Код я пробовал:

p = subprocess.Popen(cmd, 
        stdout=subprocess.PIPE, 
        stderr=subprocess.STDOUT, 
        bufsize=0) 
line = p.stdout.readline() 
while line: 
    sys.stdout.write(line) 
    sys.stdout.flush() 
    # DO OTHER STUFF 
    line = p.stdout.readline() 

Также попытался с for line in iter(p.stdout.readline, b'') вместо петли в то время как и с read(1) вместо readline(). Всегда один и тот же результат, выход задерживается на несколько секунд или минут, и сразу появляется несколько строк.

То, что я думаю, что происходит:

bufsize устанавливается в 0 (устанавливаются в 0 по умолчанию в соответствии с Документами), так что линии конвейера p.stdout должен быть доступны немедленно. Но так как p.stdout.readline() не возвращается сразу же, когда новая строка передается по трубопроводу, это означает, что она буферизована, поэтому сразу несколько строк, когда буфер окончательно сброшен на p.stdout.

Что я могу сделать, чтобы это сработало?

+0

связанные с: [ловить стандартный вывод в реальном времени от подпроцесса] (http://stackoverflow.com/q/18273962/4279) – jfs

+1

связанные: [Python подпроцесса readlines() виснет] (http://stackoverflow.com/q/12419198/4279) – jfs

+0

К сожалению, вы нашли свой последний комментарий после поиска решения. Все еще поддерживается, потому что ответ соответствует моей проблеме. – Nolhian

ответ

2

Благодаря pobrelkey, который нашел источник проблемы. Действительно, задержка связана с тем, что ребенок буферизует свою запись в stdout, потому что он не пишет tty. Ребенок использует stdio, который буферизируется при записи в tty, иначе он полностью буферизуется.

Мне удалось заставить его работать, используя pexpect вместо subprocess. pexpect использует псевдо-терминал, и это именно то, что нам нужно здесь:

p = pexpect.spawn(cmd,args,timeout=None) 
line = p.readline() 
while line: 
    sys.stdout.write(line) 
    sys.stdout.flush() 
    # DO OTHER STUFF 
    line = p.readline() 

Или еще лучше в моем случае:

p = pexpect.spawn(cmd,args,timeout=None,logfile=sys.stdout) 
line = p.readline() 
while line: 
    # DO OTHER STUFF 
    line = p.readline() 

Больше не медлите!

Больше информации о pexpect: wiki

+1

, если ребенок использует stdio, тогда вы можете заставить его переключиться на буферизацию строки с помощью ['stdbuf -oL' (или' unbuffer', 'script')] (http://unix.stackexchange.com/q/25372/ 1321). В моем ответе есть [пример кода] (http://stackoverflow.com/a/12471855/4279). См. Также [Python: чтение потокового ввода из subprocess.communicate()] (http://stackoverflow.com/q/2715847/4279) – jfs

+0

Действительно, ваш пост потрясающий, мне очень жаль, что я не нашел его, прежде чем писать эту тему , Я искал много «потоков в режиме реального времени с подпроцессом», но пропустил это. Есть ли предпочтительный метод между 'pexpect',' stdbuf' и 'tty'? – Nolhian

+1

все зависит. Для варианта 'stdbuf' требуется внешняя программа (' stdbuf'). 'pexpect' является сторонним чистым модулем Python, поэтому вам нужно изучить его API. 'pty' находится в stdlib, но это может быть труднее использовать (судя по [пример кода] (http://stackoverflow.com/a/12471855/4279)). Ни один из них не работает в Windows. – jfs

0

Сначала я должен убедиться, что сам подпроцесс не буферизует его вывод. Если подпроцесс, в свою очередь, является программой Python, перейдите к абзацу ниже, чтобы узнать, как отключить буферизацию вывода для процессов Python.

В соответствии с Python обычно проблема заключается в том, что Python по умолчанию буферизирует stderr и stdout, даже если вы явно указали .flush() его код. Решение должно пройти -u на Python при запуске вашей программы.

Кроме того, вы можете просто сделать for line in p.stdout вместо сложного цикла while.

P.S. На самом деле я попытался запустить ваш код (с cmd = ['cat', '/dev/urandom']) и без -u, и он выводил все в реальном времени; это на OS X 10.8.

+0

Просто попробовал с -u и все тот же. Я предполагаю, что cat/dev/urandom будет работать, потому что он генерирует много данных, поэтому буфер сбрасывается в конце в p.stdout. Я попытаюсь посмотреть, могу ли я выкопать больше информации. – Nolhian

+0

Я попытался с чем-то более медленным, например, например. '/ var/log/system.log', и я все равно получаю вывод плавно. На какой платформе вы находитесь на btw? –

+0

Можете ли вы представить пример кода, демонстрирующий поведение '.flush()'? – jfs

0

Если вы просто хотите, чтобы stdout вашего дочернего процесса перешел на ваш stdout, почему бы просто не наследовать дочерний процесс stdout из вашего процесса?

subprocess.Popen(cmd, stdout=None, stderr=subprocess.STDOUT) 
+0

Действительно, но я действительно делаю другие вещи, кроме вывода на стандартный вывод. Извините, я редактировал свой пост. – Nolhian

+2

Ах. Тогда это будет зависеть от того, будет ли дочерний процесс буферизировать свои записи на stdout - часто это будет происходить автоматически, если процесс не записывается в tty. В этом случае вам нужно будет изменить дочерний процесс вместо вашего скрипта или определить способ его вывода через pty (ожидание или модуль pty может помочь здесь). Сожалею. – pobrelkey

Смежные вопросы