Mi script de Python usa un subproceso para llamar a una utilidad de Linux que es muy ruidosa. Quiero almacenar todo el resultado en un archivo de registro y mostrarlo al usuario. Pensé que lo siguiente funcionaría, pero el resultado no aparece en mi aplicación hasta que la utilidad haya producido una cantidad significativa de resultados.
#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()
El comportamiento que realmente quiero es que el script de filtro imprima cada línea tal como se recibe del subproceso. Sorta como lo que tee
hace pero con código python.
¿Qué me estoy perdiendo? ¿Es esto posible?
Actualizar:
Si sys.stdout.flush()
se agrega a fake_utility.py, el código tiene el comportamiento deseado en python 3.1. Estoy usando python 2.6. Se podría pensar que usar proc.stdout.xreadlines()
funcionaría igual que py3k, pero no es así.
Actualización 2:
Aquí está el código de trabajo 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()
fuente
print line,
lugar deprint line.rstrip()
(nota: coma al final).subprocess.communicate()
Respuestas:
Ha pasado mucho tiempo desde la última vez que trabajé con Python, pero creo que el problema es con la declaración
for line in proc.stdout
, que lee toda la entrada antes de repetirla. La solución es usarreadline()
en su lugar:Por supuesto, todavía tiene que lidiar con el almacenamiento en búfer del subproceso.
Nota: de acuerdo con la documentación, la solución con un iterador debería ser equivalente a usar
readline()
, excepto el búfer de lectura anticipada, pero (o exactamente por esto) el cambio propuesto produjo resultados diferentes para mí (Python 2.5 en Windows XP).fuente
file.readline()
vs.for line in file
ver bugs.python.org/issue3907 (en resumen: se trabaja en python3, el usoio.open()
de Python 2.6+)for line in iter(proc.stdout.readline, ''):
.for line in proc.stdout
en Python 3 (no existe el error de lectura anticipada) 2.'' != b''
en Python 3 - no copie y pegue el código a ciegas - piense qué hace y cómo funciona.iter(f.readline, b'')
solución es bastante obvia (y también funciona en Python 2, si alguien está interesado). El punto de mi comentario no fue culpar a su solución (perdón si parecía así, ¡también lo leí ahora!), Sino describir la extensión de los síntomas, que son bastante graves en este caso (la mayoría de las Py2 / 3 problemas resultan en excepciones, mientras que aquí un bucle de buen comportamiento cambió para ser interminable, y la recolección de basura lucha contra la inundación de objetos recién creados, produciendo oscilaciones de uso de memoria con un período largo y gran amplitud).Un poco tarde a la fiesta, pero me sorprendió no ver cuál es la solución más simple aquí:
(Esto requiere Python 3.)
fuente
AttributeError: 'file' object has no attribute 'readable'
py2.7TextIOWrapper
o no. Simplemente puede manejar la excepción.De hecho, si resolvió el iterador, el almacenamiento en búfer ahora podría ser su problema. Podrías decirle a la pitón en el subproceso que no almacene su salida.
se convierte
He necesitado esto al llamar a python desde python.
fuente
Desea pasar estos parámetros adicionales a
subprocess.Popen
:Entonces puedes iterar como en tu ejemplo. (Probado con Python 3.5)
fuente
Una función que permite iterar sobre ambos
stdout
ystderr
simultáneamente, en tiempo real, línea por líneaEn caso de que necesite obtener el flujo de salida para ambos
stdout
ystderr
al mismo tiempo, puede usar la siguiente función.La función usa Colas para fusionar ambas tuberías de Popen en un solo iterador.
Aquí creamos la función
read_popen_pipes()
:read_popen_pipes()
en uso:fuente
También puede leer líneas sin bucle. Funciona en python3.6.
fuente
list_of_strings = [x.decode('utf-8').rstrip('\n') for x in iter(process.stdout.readlines())]
Intenté esto con python3 y funcionó, fuente
fuente
La siguiente modificación de la respuesta de Rômulo funciona para mí en Python 2 y 3 (2.7.12 y 3.6.1):
fuente
No sé cuándo esto se ha agregado al módulo de subproceso, pero con Python 3 debería estar bien usando
proc.stdout.splitlines()
:fuente