¿Cómo replicar el comportamiento de tee en Python cuando se usa el subproceso?
Estoy buscando una solución de Python que me permita guardar la salida de un comando en un archivo sin ocultarlo de la consola.
FYI: estoy preguntando sobretee (como la utilidad de línea de comandos de Unix) y no la función con el mismo nombre del módulo de herramientas de Python.
DetallesSolución Python (sin llamartee
, no está disponible en Windows)No necesito proporcionar ninguna entrada a stdin para el proceso llamadoNo tengo control sobre el programa llamado. Todo lo que sé es que generará algo en stdout y stderr y regresará con un código de salida.Para trabajar al llamar a programas externos (subproceso)Para trabajar para ambosstderr
ystdout
Ser capaz de diferenciar entre stdout y stderr porque es posible que quiera mostrar solo uno de ellos en la consola o podría intentar generar stderr con un color diferente; esto significa questderr = subprocess.STDOUT
no trabajará.Salida en vivo (progresiva): el proceso puede ejecutarse durante mucho tiempo y no puedo esperar a que termine.Código compatible con Python 3 (importante)ReferenciasAquí hay algunas soluciones incompletas que encontré hasta ahora:
http://devlishgenius.blogspot.com/2008/10/logging-in-real-time-in-python.html (mkfifo solo funciona en Unix)http://blog.kagesenshi.org/2008/02/teeing-python-subprocesspopen-output.html (no funciona en absoluto)Diagrama http://blog.i18n.ro/wp-content/uploads/2010/06/Drawing_tee_py.png
Código actual (segundo intento)#!/usr/bin/python
from __future__ import print_function
import sys, os, time, subprocess, io, threading
cmd = "python -E test_output.py"
from threading import Thread
class StreamThread ( Thread ):
def __init__(self, buffer):
Thread.__init__(self)
self.buffer = buffer
def run ( self ):
while 1:
line = self.buffer.readline()
print(line,end="")
sys.stdout.flush()
if line == '':
break
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdoutThread = StreamThread(io.TextIOWrapper(proc.stdout))
stderrThread = StreamThread(io.TextIOWrapper(proc.stderr))
stdoutThread.start()
stderrThread.start()
proc.communicate()
stdoutThread.join()
stderrThread.join()
print("--done--")
#### test_output.py ####
#!/usr/bin/python
from __future__ import print_function
import sys, os, time
for i in range(0, 10):
if i%2:
print("stderr %s" % i, file=sys.stderr)
else:
print("stdout %s" % i, file=sys.stdout)
time.sleep(0.1)
Salida realstderr 1
stdout 0
stderr 3
stdout 2
stderr 5
stdout 4
stderr 7
stdout 6
stderr 9
stdout 8
--done--
La salida esperada era tener las líneas ordenadas. Observación, no está permitido modificar el Popen para usar solo un PIPE porque en la vida real querré hacer cosas diferentes con stderr y stdout.
Además, incluso en el segundo caso, no pude obtener tiempo real, de hecho, todos los resultados se recibieron cuando terminó el proceso. Por defecto, Popen no debe usar buffers (bufsize = 0).