examples example español python subprocess

python - example - Obtención de salida en tiempo real mediante subproceso



subprocess python 3 example (12)

Estoy tratando de escribir un script de contenedor para un programa de línea de comando (svnadmin verify) que mostrará un buen indicador de progreso para la operación. Esto requiere que pueda ver cada línea de salida del programa envuelto tan pronto como se emita.

Pensé que simplemente ejecutaría el programa usando el subprocess.Popen , use stdout=PIPE , luego lea cada línea tal como entró y actúe en consecuencia. Sin embargo, cuando ejecuté el siguiente código, la salida parecía estar almacenada en algún lugar, haciendo que apareciera en dos partes, líneas 1 a 332, luego 333 a 439 (la última línea de salida)

from subprocess import Popen, PIPE, STDOUT p = Popen(''svnadmin verify /var/svn/repos/config'', stdout = PIPE, stderr = STDOUT, shell = True) for line in p.stdout: print line.replace(''/n'', '''')

Después de analizar un poco la documentación del subproceso, descubrí el parámetro bufsize en Popen , así que intenté configurar bufsize en 1 (buffer en cada línea) y 0 (sin buffer), pero ninguno de los valores parecía cambiar la forma en que se entregaban las líneas. .

En este momento estaba empezando a comprender las pajas, así que escribí el siguiente ciclo de salida:

while True: try: print p.stdout.next().replace(''/n'', '''') except StopIteration: break

pero obtuve el mismo resultado.

¿Es posible obtener la salida del programa ''en tiempo real'' de un programa ejecutado mediante un subproceso? ¿Hay alguna otra opción en Python que sea compatible con versiones anteriores (no exec* )?


Encontré esta función de "conectar y usar" here . ¡Trabajado como un encanto!

import subprocess def myrun(cmd): """from http://blog.kagesenshi.org/2008/02/teeing-python-subprocesspopen-output.html """ p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout = [] while True: line = p.stdout.readline() stdout.append(line) print line, if line == '''' and p.poll() != None: break return ''''.join(stdout)


Este es el esqueleto básico que siempre uso para esto. Hace que sea fácil implementar tiempos de espera y es capaz de lidiar con procesos de colgado inevitables.

import subprocess import threading import Queue def t_read_stdout(process, queue): """Read from stdout""" for output in iter(process.stdout.readline, b''''): queue.put(output) return process = subprocess.Popen([''dir''], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, cwd=''C://', shell=True) queue = Queue.Queue() t_stdout = threading.Thread(target=t_read_stdout, args=(process, queue)) t_stdout.daemon = True t_stdout.start() while process.poll() is None or not queue.empty(): try: output = queue.get(timeout=.5) except Queue.Empty: continue if not output: continue print(output), t_stdout.join()


Intenté esto, y por alguna razón, mientras el código

for line in p.stdout: ...

buffers agresivamente, la variante

while True: line = p.stdout.readline() if not line: break ...

no. Aparentemente, este es un error conocido: http://bugs.python.org/issue3907


Me encontré con el mismo problema hace un tiempo. Mi solución fue deshacerse de las iteraciones para el método de read , que volverá de inmediato incluso si su subproceso no se terminó de ejecutar, etc.


Problema de salida en tiempo real resuelto: Encontré un problema similar en Python, mientras capturaba la salida en tiempo real del programa c. Agregué " fflush (stdout) ;" en mi código C Funcionó para mí Aquí está el recorte del código

<< C Programa >>

#include <stdio.h> void main() { int count = 1; while (1) { printf(" Count %d/n", count++); fflush(stdout); sleep(1); } }

<< Programa de Python >>

#!/usr/bin/python import os, sys import subprocess procExe = subprocess.Popen(".//count", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) while procExe.poll() is None: line = procExe.stdout.readline() print("Print:" + line)

<< OUTPUT >> Print: Count 1 Print: Count 2 Print: Count 3

Espero eso ayude.

~ sairam


Puede dirigir la salida del subproceso a las transmisiones directamente. Ejemplo simplificado:

subprocess.run([''ls''], stderr=sys.stderr, stdout=sys.stdout)


Puede usar un iterador sobre cada byte en la salida del subproceso. Esto permite la actualización en línea (líneas que terminan con ''/ r'' sobrescribir la línea de salida anterior) del subproceso:

from subprocess import PIPE, Popen command = ["my_command", "-my_arg"] # Open pipe to subprocess subprocess = Popen(command, stdout=PIPE, stderr=PIPE) # read each byte of subprocess while subprocess.poll() is None: for c in iter(lambda: subprocess.stdout.read(1) if subprocess.poll() is None else {}, b''''): c = c.decode(''ascii'') sys.stdout.write(c) sys.stdout.flush() if subprocess.returncode != 0: raise Exception("The subprocess did not terminate correctly.")


Puedes intentar esto:

import subprocess import sys process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) while True: out = process.stdout.read(1) if out == '''' and process.poll() != None: break if out != '''': sys.stdout.write(out) sys.stdout.flush()

Si usa readline en lugar de read, habrá algunos casos donde el mensaje de entrada no se imprime. Pruébalo con un comando que requiera una entrada en línea y compruébalo por ti mismo.


Solución completa:

import contextlib import subprocess # Unix, Windows and old Macintosh end-of-line newlines = [''/n'', ''/r/n'', ''/r''] def unbuffered(proc, stream=''stdout''): stream = getattr(proc, stream) with contextlib.closing(stream): while True: out = [] last = stream.read(1) # Don''t loop forever if last == '''' and proc.poll() is not None: break while last not in newlines: # Don''t loop forever if last == '''' and proc.poll() is not None: break out.append(last) last = stream.read(1) out = ''''.join(out) yield out def example(): cmd = [''ls'', ''-l'', ''/''] proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, # Make all end-of-lines ''/n'' universal_newlines=True, ) for line in unbuffered(proc): print line example()


Usé esta solución para obtener salida en tiempo real en un subproceso. Este ciclo se detendrá tan pronto como se complete el proceso, dejando de lado la necesidad de una declaración de interrupción o posible ciclo infinito.

sub_process = subprocess.Popen(my_command, close_fds=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) while sub_process.poll() is None: out = sub_process.stdout.read(1) sys.stdout.write(out) sys.stdout.flush()


Usar pexpect [ http://www.noah.org/wiki/Pexpect ] con readlines sin bloqueo resolverá este problema. Se deriva del hecho de que las tuberías están almacenadas en un buffer, por lo que la salida de la aplicación está siendo amortiguada por la tubería, por lo tanto, no se puede acceder a esa salida hasta que se llene la memoria intermedia o el proceso se muera.


p = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=1) for line in iter(p.stdout.readline, b''''): print line, p.stdout.close() p.wait()