2009-08-15 25 views
5

Quiero capturar stdout de un proceso de ejecución largo-ish iniciado a través de subprocess.Popen(...) entonces estoy usando stdout=PIPE como un arg.Python: captura Popen stdout AND display en la consola?

Sin embargo, debido a que es un proceso prolongado, también deseo enviar el resultado a la consola (como si no lo hubiera procesado) para darle al usuario del guión la idea de que todavía está funcionando.

¿Es esto posible?

Saludos.

+0

duplicado: http://stackoverflow.com/questions/803265/getting-realtime-output-using-subprocess y HTTP : //stackoverflow.com/questions/874815/how-do-i-get-real-time-information-back-from-a-subprocess-popen-in-python-2-5 –

+0

related: [Python: lectura de transmisión entrada de subprocess.communicate()] (http://stackoverflow.com/a/17698359/4279) – jfs

Respuesta

1

¿Puede simplemente print como lo leyó desde la tubería?

+0

¡Ah! (¡momento de la bombilla!) - p.stdout es un archivo como objeto. ¿Entonces solo necesito llamar a readline() en un bucle? ¿Derecha? ¿Cuál es la mejor manera de salir del ciclo cuando p.stdout devuelve una cadena vacía? ¿Debo seguir haciendo p.wait() solo para estar seguro? –

+1

@Steve: una cadena vacía devuelta por la lectura p.stdout significa que el niño ha cerrado su stdout, pero no necesariamente que está terminado. p.wait() esperará a que finalice. – RichieHindle

5

El almacenamiento en búfer de su subproceso de ejecución prolongada probablemente se esté ejecutando hará que la salida de su consola sea desigual y su UX muy malo. Le sugiero que, en su lugar, considere usar pexpect (o, en Windows, wexpect) para vencer dicho almacenamiento en búfer y obtener un resultado uniforme y regular del subproceso. Por ejemplo (en casi cualquier sistema UNIX-y, después de instalar pexpect):

>>> import pexpect 
>>> child = pexpect.spawn('/bin/bash -c "echo ba; sleep 1; echo bu"', logfile=sys.stdout); x=child.expect(pexpect.EOF); child.close() 
ba 
bu 
>>> child.before 
'ba\r\nbu\r\n' 

El ba y bu vendrán con la sincronización apropiada (alrededor de un segundo entre ellos). Tenga en cuenta que la salida no está sujeta al procesamiento normal de la terminal, por lo que los retornos de carro se dejan allí. Necesitará procesar la cadena usted mismo (¡simplemente un .replace! -) si necesita \n como fin de línea marcadores (la falta de procesamiento es importante solo en el caso de que el subproceso escriba datos binarios en su salida estándar - ¡esto garantiza que todos los datos queden intactos! -).

1

Alternativamente, puede canalizar su proceso en T y capturar solo una de las transmisiones. Algo a lo largo de las líneas de sh -c 'process interesting stuff' | tee /dev/stderr.

Por supuesto, esto solo funciona en sistemas tipo Unix.

+1

Desafortunadamente todavía está sujeto a sacudidas causadas por el almacenamiento en búfer :-(. –

2

puntos de comentario de S. Lott a Getting realtime output using subprocess y Real-time intercepting of stdout from another process in Python

Soy curioso que la respuesta de Alex aquí es diferente de su respuesta 1085071. Mis simples pequeños experimentos con las respuestas en las otras dos preguntas que se hace referencia ha dado buenos resultados ...

Fui y miré a wexpect según la respuesta de Alex anterior, pero tengo que decir que al leer los comentarios en el código no me quedó muy buena impresión sobre su uso.

Supongo que la meta-pregunta aquí es cuándo pexpect/wexpect será una de las baterías incluidas?

1

Inspirado por pty.openpty() sugerencia en alguna parte anterior, probado en python2.6, linux. La publicación ya que tomó un tiempo para hacer este trabajo correctamente, w/o de amortiguación ...

def call_and_peek_output(cmd, shell=False): 
    import pty, subprocess 
    master, slave = pty.openpty() 
    p = subprocess.Popen(cmd, shell=shell, stdin=None, stdout=slave, close_fds=True) 
    os.close(slave) 
    line = "" 
    while True: 
     try: 
      ch = os.read(master, 1) 
     except OSError: 
      # We get this exception when the spawn process closes all references to the 
      # pty descriptor which we passed him to use for stdout 
      # (typically when it and its childs exit) 
      break 
     line += ch 
     sys.stdout.write(ch) 
     if ch == '\n': 
      yield line 
      line = "" 
    if line: 
     yield line 

    ret = p.wait() 
    if ret: 
     raise subprocess.CalledProcessError(ret, cmd) 

for l in call_and_peek_output("ls /", shell=True): 
    pass 
Cuestiones relacionadas