Meu script python usa subprocesso para chamar um utilitário linux que é muito barulhento. Eu quero armazenar toda a saída em um arquivo de log e mostrar algumas delas para o usuário. Eu pensei que o seguinte funcionaria, mas a saída não aparece no meu aplicativo até que o utilitário tenha produzido uma quantidade significativa de saída.
#fake_utility.py, just generates lots of output over time
import time
i = 0
while True:
print hex(i)*512
i += 1
time.sleep(0.5)
#filters output
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
for line in proc.stdout:
#the real code does filtering here
print "test:", line.rstrip()
O comportamento que eu realmente quero é que o script de filtro imprima cada linha conforme é recebida do subprocesso. Tipo como o que tee
faz, mas com código python.
o que estou perdendo? Isso é possível?
Atualizar:
Se a sys.stdout.flush()
for adicionado ao fake_utility.py, o código terá o comportamento desejado no python 3.1. Estou usando o python 2.6. Você pensaria que o uso proc.stdout.xreadlines()
funcionaria da mesma forma que o py3k, mas não.
Atualização 2:
Aqui está o código de trabalho mínimo.
#fake_utility.py, just generates lots of output over time
import sys, time
for i in range(10):
print i
sys.stdout.flush()
time.sleep(0.5)
#display out put line by line
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
#works in python 3.0+
#for line in proc.stdout:
for line in iter(proc.stdout.readline,''):
print line.rstrip()
fonte
print line,
vez deprint line.rstrip()
(nota: vírgula no final).subprocess.communicate()
Respostas:
Já faz muito tempo desde a última vez que trabalhei com Python, mas acho que o problema está na declaração
for line in proc.stdout
, que lê toda a entrada antes de iterá-la. A solução é usarreadline()
:É claro que você ainda precisa lidar com o buffer do subprocesso.
Nota: de acordo com a documentação, a solução com um iterador deve ser equivalente ao uso
readline()
, exceto pelo buffer de leitura antecipada, mas (ou exatamente por isso) a alteração proposta produziu resultados diferentes para mim (Python 2.5 no Windows XP).fonte
file.readline()
vs.for line in file
ver bugs.python.org/issue3907 (em suma: ele funciona em Python3; usoio.open()
em Python 2.6+)for line in iter(proc.stdout.readline, ''):
.for line in proc.stdout
no Python 3 (não existe o bug de leitura antecipada) 2.'' != b''
no Python 3 - não copie e cole o código cegamente - pense no que ele faz e como funciona.iter(f.readline, b'')
solução é bastante óbvia (e também funciona no Python 2, se alguém estiver interessado). O objetivo do meu comentário não foi culpar sua solução (desculpe se ela apareceu assim, eu li isso também agora!), Mas descrever a extensão dos sintomas, que são bastante graves nesse caso (a maioria dos Py2 / 3 questões resultam em exceções, enquanto aqui um loop bem-comportado mudou para ser infinito, e a coleta de lixo luta para combater a inundação de objetos recém-criados, produzindo oscilações no uso da memória com longo período e grande amplitude).Um pouco atrasado para a festa, mas fiquei surpreso ao não ver o que acho a solução mais simples aqui:
(Isso requer Python 3.)
fonte
AttributeError: 'file' object has no attribute 'readable'
py2.7TextIOWrapper
ou não. Você pode simplesmente lidar com a exceção.De fato, se você selecionou o iterador, o armazenamento em buffer agora pode ser seu problema. Você poderia dizer ao python no subprocesso para não armazenar em buffer sua saída.
torna-se
Eu preciso disso ao chamar python de dentro de python.
fonte
Você deseja passar esses parâmetros extras para
subprocess.Popen
:Então você pode iterar como no seu exemplo. (Testado com Python 3.5)
fonte
Função que permite iterar simultaneamente
stdout
estderr
simultaneamente, em tempo real, linha por linhaCaso precise obter o fluxo de saída para ambos
stdout
estderr
ao mesmo tempo, você pode usar a seguinte função.A função usa Filas para mesclar os dois pipes do Popen em um único iterador.
Aqui criamos a função
read_popen_pipes()
:read_popen_pipes()
em uso:fonte
Você também pode ler linhas sem loop. Funciona em python3.6.
fonte
list_of_strings = [x.decode('utf-8').rstrip('\n') for x in iter(process.stdout.readlines())]
Eu tentei isso com python3 e funcionou, fonte
fonte
A seguinte modificação da resposta de Rômulo funciona para mim no Python 2 e 3 (2.7.12 e 3.6.1):
fonte
Não sei quando isso foi adicionado ao módulo de subprocesso, mas com o Python 3 você deve usar bem
proc.stdout.splitlines()
:fonte