2009-05-17 15 views
44

Me gustaría utilizar el módulo de subproceso de la siguiente manera:¿Cómo puedo obtener información 'en tiempo real' de volver de un subprocess.Popen en Python (2.5)

  1. crear un nuevo proceso que potencialmente lleva mucho tiempo ejecutarlo
  2. captura stdout (o stderr, o potencialmente ambos, juntos o por separado)
  3. datos de proceso del subproceso ya que viene en, eventos quizá disparando en cada línea recibida (en wxPython decir) o simplemente imprimirlos por ahora.

He creado procesos con Popen, pero si uso comunicar() los datos me llegan a la vez, una vez que el proceso ha finalizado.

Si creo un hilo separado que hace un bloqueo de readline()myprocess.stdout (usando stdout = subprocess.PIPE) no consigo ninguna línea con este método tampoco, hasta que el proceso termina. (no importa lo que configuro como bufsize)

¿Hay alguna manera de lidiar con esto que no sea horrible, y funciona bien en múltiples plataformas?

+2

myprocess.stdout.readline() debería funcionar. ¿Puedes mostrarnos tu código? –

+0

Las lecturas sin búfer de popen_obj.stdout() deberían funcionar, pero si no le importa estar limitado a plataformas con soporte PTY, su aplicación podría ser adecuada para la biblioteca Pexpect. –

+1

Esta es una gran pregunta y todavía parece no tener respuesta, al menos para el requisito de "funciona bien en múltiples plataformas". –

Respuesta

7

stdout se almacenará en el búfer, por lo que no obtendrá nada hasta que se llene ese búfer, o el subproceso salga.

Puede intentar enjuagar stdout desde el subproceso, o usar stderr, o cambiar la salida estándar en modo no amortiguado.

+2

¿No debería estar sin búfer por defecto? Al menos con bufsize = 0? – Albert

+2

@Albert: el búfer está * dentro * del subproceso, por ejemplo, el búfer 'stdio'. Nada fuera del proceso secundario ve esos datos hasta que vacía su búfer stdout. Aquí hay algunas soluciones para [el problema del almacenamiento en búfer] (http://stackoverflow.com/a/20509641/4279) – jfs

2

Parece que el problema podría ser el uso de la salida almacenada por el subproceso; si se crea una cantidad relativamente pequeña de salida, podría almacenarse en el búfer hasta que salga el subproceso. Algunos antecedentes se puede encontrar here:

8

actualización con el código que aparece no trabajar (en las ventanas de todos modos)

class ThreadWorker(threading.Thread): 
    def __init__(self, callable, *args, **kwargs): 
     super(ThreadWorker, self).__init__() 
     self.callable = callable 
     self.args = args 
     self.kwargs = kwargs 
     self.setDaemon(True) 

    def run(self): 
     try: 
      self.callable(*self.args, **self.kwargs) 
     except wx.PyDeadObjectError: 
      pass 
     except Exception, e: 
      print e 



if __name__ == "__main__": 
    import os 
    from subprocess import Popen, PIPE 

    def worker(pipe): 
     while True: 
      line = pipe.readline() 
      if line == '': break 
      else: print line 

    proc = Popen("python subprocess_test.py", shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) 

    stdout_worker = ThreadWorker(worker, proc.stdout) 
    stderr_worker = ThreadWorker(worker, proc.stderr) 
    stdout_worker.start() 
    stderr_worker.start() 
    while True: pass 
+0

Esta es claramente la mejor respuesta. ¡Gracias por mostrarme este tipo de 'pipa' en Python! – Mapad

+0

Esta es una gran respuesta, y funciona para mí. La clave es que las lecturas pueden bloquearse sin problemas debido a los hilos. –

+0

'while True: time.sleep (1)' es mejor que un bucle de espera ocupado que consume toda tu CPU. – ReneSac

2

Esto es lo que funcionó para mí:

cmd = ["./tester_script.bash"] 
p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
while p.poll() is None: 
    out = p.stdout.readline() 
    do_something_with(out, err) 

En su caso, usted podría tratar de pasar una referencia a la sub-proceso para el subproceso de trabajo, y hacer el sondeo en el interior del hilo. No sé cómo se comportará cuando dos subprocesos sondean (e interactúan) el mismo subproceso, pero puede funcionar.

También tenga en cuenta que el while p.poll() is None: está diseñado como está. Do no reemplazarlo con while not p.poll() como en python 0 (el código de retorno para la terminación exitosa) también se considera False.

+0

No estoy seguro de si hay algo que me falta aquí, pero parece que tu código realmente está bloqueando: ese bucle "while" significa que cualquiera que sea el resultado de esa función, no se devolverá hasta que p.poll() es Ninguno. – pgcd

+0

Sí. Esto es un bloqueo. Supongo que cuando respondí esto, no noté la parte de la pregunta. Aun así, mi respuesta aquí no está del todo "actualizada". El principal problema es que la salida está almacenada (como se mencionó en otra parte), y no lo menciono aquí. Lo dejaré aquí para la posteridad, pero otras respuestas enviadas son mejores. – exhuma

1

Me he encontrado con este problema también. El problema ocurre porque también estás tratando de leer stderr. Si no hay errores, entonces tratar de leer desde stderr bloquearía.

En Windows, no hay una forma sencilla de sondear los descriptores de archivos (solo conectores Winsock).

Así que una solución es no intentar leer de stderr.

1

El uso de pexpect [http://www.noah.org/wiki/Pexpect] con líneas de lectura 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.

1

Esto parece ser una limitación bien conocida de Python, vea PEP 3145 y tal vez otros.

1

Leer un carácter a la vez: http://blog.thelinuxkid.com/2013/06/get-python-subprocess-output-without.html

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() 
+0

parece ser una respuesta duplicada. Ver [mi comentario anterior] (http: // stackoverflow.com/questions/803265/getting-realtime-output-using-subprocess/15288921 # comment41473849_15288921) – jfs

0

Usando subprocess.Popen, que puede ejecutar el .exe de uno de mis proyectos de C# y redirigir la salida a mi archivo de Python. Ahora puedo print() toda la información que se envía a la consola C# (usando Console.WriteLine()) a la consola de Python.

código Python:

from subprocess import Popen, PIPE, STDOUT 

p = Popen('ConsoleDataImporter.exe', stdout = PIPE, stderr = STDOUT, shell = True) 

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

Esto consigue la salida de la consola de mi .NET línea de proyecto por línea, ya que se crea y se rompe fuera de la envolvente bucle while a la terminación del proyecto. Me imagino que esto también funcionaría para dos archivos de Python.

Cuestiones relacionadas