2012-01-12 10 views
5

Soy nuevo en python, habiendo usado Perl durante años. Una cosa típica que hago todo el tiempo es que perl abra un comando como una tubería y asigne su salida a una variable local para su procesamiento. En otras palabras:procesamiento de salida continua de un comando en python

"open CMD, "$command|"; 
$output=<CMD>; 

a piece of cake. Creo que puedo hacer algo similar en python de esta manera:

args=[command, args...] 
process=subprocess.Popen(args, stdout=subprocess.PIPE) 
output=process.communicate() 

hasta ahora todo bien. Ahora, para la gran pregunta ...

Si desactivo ese comando usando un ssh en múltiples plataformas, entonces puedo monitorear los descriptores en perl dentro de un ciclo de selección para procesar los resultados a medida que ingresan. Encontré el Python selecciona y encuesta los módulos, pero no estoy muy seguro de cómo usarlos. La documentación dice que la encuesta tomaría un control de archivo, pero cuando trato de pasar la variable 'proceso' anterior a poll.register() me sale un error que debe ser un int o tener un método fileno(). Desde Popen() utiliza la salida estándar, intenté llamar

poll.register(process.stdout) 

y ya no genera un error, sino que simplemente se cuelga.

¿Alguna sugerencia/indicación de cómo hacer algo como esto?

+0

proceso.stdout sería el manejador de archivo para ese objeto de proceso. No estoy seguro de si eso es todo lo que necesita para que funcione con poll/select, no los he usado. – AdamKG

+0

Además, tenga en cuenta que Popen.communicate() bloquea hasta EOF; probablemente querrá deshacerse de eso. – AdamKG

+0

ahh, lo entiendo. claramente NO quiero bloquear. muchos nuevos constructos para tratar de envolver mi cerebro. - marca –

Respuesta

7

Usando select.poll: Es necesario pass objects with a fileno method or real file descriptors (integers):

import os, sys, select, subprocess 

args = ['sh', '-c', 'while true; do date; sleep 2; done'] 
p1 = subprocess.Popen(args, stdout=subprocess.PIPE) 
p2 = subprocess.Popen(args, stdout=subprocess.PIPE) 

while True: 
    rlist, wlist, xlist = select.select([p1.stdout, p2.stdout], [], []) 
    for stdout in rlist: 
     sys.stdout.write(os.read(stdout.fileno(), 1024)) 

verás que hacer una pausa cada dos segundos y luego generar más producción, ya que viene disponible. El "truco" es que p1.stdout es un objeto similar a un archivo normal con un método fileno que devuelve el número del descriptor del archivo. Esto es todo lo que necesita el select.

Tenga en cuenta que estoy leyendo desde stdout usando os.read en lugar de simplemente llamar al stdout.read. Esto se debe a que una llamada como stdout.read(1024) hará que su programa espere hasta que se haya leído el número solicitado de bytes. Se devuelven menos bytes cuando se alcanza EOF, pero como nunca se alcanza el EOF, la llamada stdout.read se bloqueará hasta que se hayan leído al menos 1024 bytes.

Esto es diferente de la función os.read, que no tiene reparos en regresar temprano cuando hay menos bytes disponibles; regresa directamente con lo que está disponible. En otras palabras, recuperar menos de 1024 bytes desde os.read(stdout.fileno(), 1024) no es una señal de que se haya cerrado stdout.

Usando select.epoll es casi idéntico, excepto que se obtiene un descriptor de archivo "en bruto" (FD) de nuevo que necesita os.read a ser capaz de leer a partir de:

import os, sys, select, subprocess 

args = ['sh', '-c', 'while true; do date; sleep 2; done'] 
p1 = subprocess.Popen(args, stdout=subprocess.PIPE) 
p2 = subprocess.Popen(args, stdout=subprocess.PIPE) 

poll = select.poll() 
poll.register(p1.stdout) 
poll.register(p2.stdout) 

while True: 
    rlist = poll.poll() 
    for fd, event in rlist: 
     sys.stdout.write(os.read(fd, 1024)) 

Un cerrado FD es señalado por el select.POLLHUP evento devuelto. Luego puede llamar al método unregister y finalmente salir del circuito cuando todos los FD están cerrados.

Finalmente, permítanme señalar que, por supuesto, podría hacer un diccionario con una asignación de descriptores de archivo a los objetos similares a archivos, y de ahí a los procesos que inició.

+0

pulido, pero no lo veo esperando en ninguna parte. , el comando que estoy ejecutando es "collectl", una herramienta de supervisión que escribí en perl –

+0

este sitio web es un hecho real. Quería proporcionar una respuesta mejor formateada y no me deja responder mi propia pregunta. Así que ahora tengo que escribir una respuesta difícil de analizar. Tu solución funciona PERO la selección no está durmiendo. se despierta continuamente y la lectura a continuación devuelve "Ninguno", que luego tengo que ignorar. Me adelgazo si pruebas tu ejemplo con algo así como un comando ps que genera mucho más salida. Verás lo que quiero decir: marca –

+1

. He agregado algunas declaraciones de impresión que puedes habilitar. Cuando hago eso aquí, veo que está esperando como debería durante dos segundos, y luego lee algunos bytes de cada uno de los descriptores de archivos listos. Acabo de cambiar la respuesta para leer * hasta * 1024 bytes y parece funcionar bien aquí, lo que significa que la llamada 'os.read' regresa cada vez que el FD se queda sin bytes. –

2
import subprocess 

p = subprocess.Popen('apt-get autoclean', stdout=subprocess.PIPE, stderr = None, shell=True) 

for line in iter(p.stdout.readline, ''): 

    print line 

p.stdout.flush() 
p.stdout.close() 

print ("Done") 
+0

no use 'shell = True' a menos que sea necesario. Su código se bloqueará si alguien cambia 'print line' a' print (line) 'y lo ejecuta en Python 3.' print line' duplica todas las nuevas líneas. Use 'bufsize = 1' para mejorar el rendimiento. '.flush()' no es necesario aquí. Puede usar 'with'-statement para cerrar el conducto. Llame a 'p.wait()' para evitar zombies. Vea [esta respuesta (cada carácter (incluso una coma) es por alguna razón)] (http://stackoverflow.com/a/17698359/4279) – jfs

Cuestiones relacionadas