2011-04-07 21 views
21

Necesito ejecutar un comando que produce una gran cantidad de salida y toma mucho tiempo para ejecutarse (> 30 minutos). Estaba pensando en usar el subproceso. Tengo libertad para hacerlo. Necesito capturar la salida del comando, entonces paso PIPE a stdout y stderr.Necesito evitar el interbloqueo de subproceso sin comunicarme

Un problema de interbloqueo al usar Popen.wait() está bien documentado en muchos foros, por lo que Popen.communicate() es la forma propuesta de evitar el interbloqueo. El problema con esa solución es que los bloques de comunicación() hasta que se complete el comando. Necesito imprimir todo lo que llega en stdout mientras se ejecuta el comando. Si no hay salida después de 20 minutos, se ejecutará la ejecución del script.

Estas son algunas limitaciones que tengo que respetar:

  • versión 2.4.2 Mi Python es y no puedo actualizar.
  • Si la solución es todavía utilizar subproceso, necesito pasar subprocess.PIPE a todos std maneja para evitar este error: http://bugs.python.org/issue1124861

¿Hay una manera de hacerlo?

+1

(Viniendo de Google?) Todas las tuberías serán un punto muerto cuando uno de los tubos de búfer se llena y no se lee. p.ej. estancamiento estándar cuando stderr está lleno. Nunca pase un PIPE que no intente leer. –

Respuesta

12

import os 
from subprocess import PIPE, STDOUT, Popen 

lines = [] 
p = Popen(cmd, bufsize=1, stdin=open(os.devnull), stdout=PIPE, stderr=STDOUT) 
for line in iter(p.stdout.readline, ''): 
     print line,   # print to stdout immediately 
     lines.append(line) # capture for later 
p.stdout.close() 
p.wait() 
+1

Esto era lo que necesitaba. ¡Muchas gracias! ¡Acabas de resolver un problema que me llevó un día completo de trabajo para investigar! – GDICommander

+2

@GDICommander: tenga cuidado, el código podría caer stderr en [Wine] (http://www.winehq.org/). Funciona bien en Ubuntu. Asegúrese de probarlo en Windows. – jfs

+0

si quiero enviar una entrada? ejemplo: p.stdin.write ("SÍ") –

6

¿Has probado pexpect?

+1

Desafortunadamente, pexpect solo funciona en sistemas tipo Unix. Necesito una solución que sea multiplataforma. – GDICommander

0

Se podría considerar el uso de múltiples hilos. Asignar un hilo para leer desde la salida estándar, uno de stderr, y el uso de un tercer hilo para detectar el tiempo de espera:

while time.time() - last_output_time < 20 * 60: 
    time.sleep(20 * 60 - (time.time() - last_output_time)) 
print 'No output detected in the last 20 minutes. Terminating execution' 
sys.exit(1) 
1

Para evitar los buffers de tubo de llenado arriba, apenas lanzar un subproceso de fondo en el proceso padre. Ese hilo puede leer continuamente desde stdout (y stderr) para evitar que se llenen los buffers de tuberías, o puede invocar communicate() desde él. De cualquier manera, el hilo principal es libre de continuar con el procesamiento ordinario y el proceso secundario no se bloqueará en una operación de salida.

La conversión de una operación IO sincrónica en una asíncrona (desde el punto de vista del hilo principal) es uno de los mejores casos de uso para hilos. Incluso los marcos asincrónicos como Twisted a veces lo usan como una solución de último recurso cuando no hay una interfaz asincrónica nativa disponible para una operación determinada.

Cuestiones relacionadas