Lea la entrada de transmisión desde subprocess.communicate ()

Estoy usando subprocess.communicate() Python para leer la salida estándar de un proceso que se ejecuta durante aproximadamente un minuto.

¿Cómo puedo imprimir cada línea de la salida estándar de ese proceso en forma de transmisión, de modo que pueda ver la salida a medida que se genera, pero aún así el locking del proceso termina antes de continuar?

Aparece subprocess.communicate() para dar todos los resultados a la vez.

Tenga en cuenta, creo que el método de JF Sebastian (a continuación) es mejor.


Aquí hay un ejemplo simple (sin verificar errores):

 import subprocess proc = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, ) while proc.poll() is None: output = proc.stdout.readline() print output, 

Si ls termina demasiado rápido, entonces el ciclo while puede terminar antes de que haya leído todos los datos.

Puede capturar el rest en la salida estándar de esta manera:

 output = proc.communicate()[0] print output, 

Para obtener la línea de salida del subproceso línea por línea tan pronto como el subproceso vacía su búfer stdout:

 #!/usr/bin/env python2 from subprocess import Popen, PIPE p = Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1) with p.stdout: for line in iter(p.stdout.readline, b''): print line, p.wait() # wait for the subprocess to exit 

iter() se usa para leer líneas tan pronto como se escriben para solucionar el error de lectura anticipada en Python 2 .

Si el subproceso stdout utiliza un bloque de búfer en lugar de un búfer de línea en modo no interactivo (que lleva a un retraso en la salida hasta que el búfer del niño esté lleno o vaciado explícitamente por el niño), podría intentar forzar una salida sin búfer usando pexpect , pty modules o stdbuf , stdbuf , utilidades de script , consulte P: ¿Por qué no usar simplemente una tubería (popen ())?


Aquí está el código de Python 3:

 #!/usr/bin/env python3 from subprocess import Popen, PIPE with Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1, universal_newlines=True) as p: for line in p.stdout: print(line, end='') 

Nota: A diferencia de Python 2, que genera las secuencias de bytes de los subprocesos tal como están; Python 3 usa el modo de texto (la salida de cmd se decodifica usando la locale.getpreferredencoding(False) ).

Creo que la forma más sencilla de recostackr resultados de un proceso en forma de transmisión es la siguiente:

 import sys from subprocess import * proc = Popen('ls', shell=True, stdout=PIPE) while True: data = proc.stdout.readline() # Alternatively proc.stdout.read(1024) if len(data) == 0: break sys.stdout.write(data) # sys.stdout.buffer.write(data) on Python 3.x 

La función readline() o read() solo debería devolver una cadena vacía en EOF, después de que el proceso haya finalizado; de lo contrario, se bloqueará si no hay nada que leer ( readline() incluye la nueva línea, por lo que en las líneas vacías, devuelve ” \norte”). Esto evita la necesidad de una llamada de communicate() final communicate() incómoda después del bucle.

En archivos con líneas muy largas, puede ser preferible read() para reducir el uso máximo de memoria: el número que se le pasa es arbitrario, pero excluirlo da como resultado la lectura de toda la salida de la tubería de una vez, lo que probablemente no sea deseable.

Si desea un enfoque de no locking, no use process.communicate() . Si establece el stdout subprocess.Popen() en PIPE , puede leer desde process.stdout y verificar si el proceso todavía se ejecuta con process.poll() .

Si simplemente está intentando pasar la salida en tiempo real, es difícil que sea más simple que esto:

 import subprocess # This will raise a CalledProcessError if the program return a nonzero code. # You can use call() instead if you don't care about that case. subprocess.check_call(['ls', '-l']) 

Consulte los documentos para subprocess.check_call () .

Si necesitas procesar la salida, seguro, haz un bucle en ella. Pero si no lo haces, hazlo simple.

Edición: JF Sebastian señala que los valores predeterminados para los parámetros stdout y stderr pasan a sys.stdout y sys.stderr, y que esto fallará si sys.stdout y sys.stderr han sido reemplazados (por ejemplo, para capturar la salida en pruebas).

 myCommand="ls -l" cmd=myCommand.split() # "universal newline support" This will cause to interpret \n, \r\n and \r equally, each as a newline. p = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True) while True: print(p.stderr.readline().rstrip('\r\n'))