2010-06-29 13 views
12

Una vez más, la misma pregunta.
La razón es - Todavía no puedo hacer que funcione después de leer lo siguiente:subprocess.Popen.stdout - lectura de stdout en tiempo real (de nuevo)

Mi caso es que tengo una aplicación de consola escrita en C, tomemos por ejemplo este código en un bucle:

tmp = 0.0; 
printf("\ninput>>"); 
scanf_s("%f",&tmp); 
printf ("\ninput was: %f",tmp); 

Lee continuamente algunas entradas y escribe alguna salida.

Mi código python para interactuar con él es la siguiente:

p=subprocess.Popen([path],stdout=subprocess.PIPE,stdin=subprocess.PIPE) 
p.stdin.write('12345\n') 
for line in p.stdout: 
    print(">>> " + str(line.rstrip())) 
    p.stdout.flush() 

Hasta ahora cada vez que leo forma p.stdout siempre espera hasta que se termina el proceso y luego da salida a una cadena vacía. He intentado muchas cosas, pero aún así el mismo resultado.

Intenté Python 2.6 y 3.1, pero la versión no importa, solo necesito hacer que funcione en alguna parte.

+1

Estoy exactamente en la misma situación, habiendo leído todas esas preguntas y todavía no encontré nada elegante que funcione. –

+0

relacionado: [Cómo interactuar correctamente con un proceso utilizando el módulo de subproceso] (http://stackoverflow.com/q/443057/4279) – jfs

+0

Vine aquí con la misma pregunta, encontré la respuesta aquí http://stackoverflow.com/q/375427/168034 – phunehehe

Respuesta

3

Intentar escribir y leer desde las tuberías a un subproceso es complicado debido al almacenamiento en búfer predeterminado en ambas direcciones. Es extremadamente fácil encontrar un punto muerto donde uno u otro proceso (padre o hijo) lee desde un búfer vacío, escribe en un búfer completo o realiza una lectura de bloqueo en un búfer que está esperando datos antes de que las bibliotecas del sistema lo vacíen.

Para cantidades de datos más modestas, el método Popen.communicate() podría ser suficiente. Sin embargo, para datos que exceden su almacenamiento en búfer, probablemente consiga procesos estancados (similar a lo que ya está viendo)

Es posible que desee buscar detalles sobre el uso del módulo fcntl y hacer una u otra (o ambos) de sus descriptores de archivos sin bloqueo. En ese caso, por supuesto, deberá envolver todas las lecturas y/o escrituras en los descriptores de archivos en el manejo de excepciones apropiado para manejar los eventos "EWOULDBLOCK". (No recuerdo la excepción exacta de Python que se plantea para estos).

Un enfoque completamente diferente sería para su padre para utilizar el módulo select y os.fork() ... y para que el proceso hijo execve() el programa de destino después de manipular directamente cualquier archivo de ING dup(). (Básicamente, volvería a implementar partes de Popen() pero con un descriptor de descriptor de archivo primario distinto (PIPE)

Por cierto, .communicate, al menos en las bibliotecas estándar 2.5 y 2.6 de Python, solo manejará unos 64K de datos remotos (en Linux y FreeBSD). Este número puede variar en función de varios factores (posiblemente incluyendo las opciones de compilación utilizadas para compilar su intérprete de Python, o la versión de libc vinculada a él). No está simplemente limitado por la memoria disponible (a pesar de JF La afirmación de Sebastián de lo contrario), pero se limita a un valor mucho más pequeño.

+0

¿Podría proporcionarnos una implementación de ejemplo? Acabo de jugar un poco con el módulo 'Queue', pero no funciona en absoluto. – Philipp

+1

+1 por mencionar el almacenamiento en búfer como el principal culpable, ver también [¿Por qué no usar simplemente un tubo (popen())?] (Http://www.noah.org/wiki/pexpect#Q:_Why_not_just_use_a_pipe_.28popen.28.29 .29.3F) de 'pexpect' docs. Pero la respuesta es demasiado pesimista, es decir, '.communicate()' funcionaría para cualquier dato que pueda caber en la memoria (mucho más grande que los tamaños de buffer de tubería). Además 'fcntl' (probablemente) no funcionará en Windows y no es necesario de todos modos. – jfs

+0

['.communicate()' funciona] (http://ideone.com/gQbDPT). Las versiones anteriores probablemente tenían un error que ahora se soluciona. – jfs

1

El argumento bufsize=256 impide 12345\n de ser enviado al proceso hijo en un trozo más pequeño que 256 bytes, como será al omitir bufsize o insertar p.stdin.flush() después de p.stdin.write(). El comportamiento predeterminado es el almacenamiento en línea de la memoria intermedia.

En cualquier caso, al menos debe ver una línea vacía antes de bloquear como se emitió en su ejemplo con el primer printf(\n...).

0

Su ejemplo particular no requiere interacción "en tiempo real". Las siguientes obras:

from subprocess import Popen, PIPE 

p = Popen(["./a.out"], stdin=PIPE, stdout=PIPE) 
output = p.communicate(b"12345")[0] # send input/read all output 
print output, 

donde a.out es su programa de ejemplo C.

En general, para una interacción basada en el diálogo con un subproceso que podría utilizar pexpect módulo (o sus análogos en Windows):

import pexpect 

child = pexpect.spawn("./a.out") 
child.expect("input>>") 
child.sendline("12345.67890") # send a number 
child.expect(r"\d+\.\d+") # expect the number at the end 
print float(child.after) # assert that we can parse it 
child.close() 
-1

que tenía el mismo problema, y ​​"proc.communicate()" no lo resuelve porque espera la finalización del proceso.

Así que aquí es lo que está funcionando para mí, en de Windows con Python 3.5.1:

import subprocess as sp 
myProcess = sp.Popen(cmd, creationflags=sp.CREATE_NEW_PROCESS_GROUP,stdout=sp.PIPE,stderr=sp.STDOUT) 
while i<40: 
     i+=1 
     time.sleep(.5) 
     out = myProcess.stdout.readline().decode("utf-8").rstrip() 

supongo creationflags y otros argumentos no son obligatorias (pero no tengo tiempo para prueba), así que esta sería la sintaxis mínima:

myProcess = sp.Popen(cmd, stdout=sp.PIPE) 
for i in range(40) 
      time.sleep(.5) 
      out = myProcess.stdout.readline()