2010-04-07 16 views
7

Quería usar un python equivalente a canalizar algunos comandos de shell en perl. Algo así como la versión python de open (PIPE, "command |").Limpieza de Popen de Python

que ir al módulo de subproceso y probar este:

p = subprocess.Popen("zgrep thingiwant largefile", shell=True, stdout=subprocess.PIPE) 

Esto funciona para la lectura de la salida de la misma manera que lo haría en Perl, pero no limpia a sí mismo. Cuando salgo de la intérprete, consigo

grep: writing output: Broken pipe 

vomitó todo Stderr unos pocos millones de veces. Supongo que ingenuamente esperaba que todo esto se ocupara de mí, pero eso no es cierto. Llamar a terminar o matar en p no parece ayudar. Mire la tabla de procesos, veo que esto mata el proceso/bin/sh, pero deja al niño gzip en su lugar para quejarse sobre la tubería rota.

¿Cuál es la forma correcta de hacerlo?

+1

¿Saldrá del intérprete antes de que su subproceso 'p' haya terminado? – physicsmichael

Respuesta

9

El problema es que el pipe está lleno. El subproceso se detiene, esperando a que la tubería se vacíe, pero luego su proceso (el intérprete de Python) se detiene, rompiendo su extremo de la tubería (de ahí el mensaje de error).

p.wait() no le ayudará:

Advertencia Esto callejón sin salida si el proceso hijo genera suficiente salida a un tubo de salida estándar o stderr tal que bloquea la espera de la memoria intermedia de la tubería del sistema operativo para aceptar más datos. Use communicate() para evitar eso.

http://docs.python.org/library/subprocess.html#subprocess.Popen.wait

p.communicate() no le ayudará:

Nota los datos leídos se tampona en la memoria, por lo que no utilice este método si el tamaño de los datos es grande o ilimitado.

http://docs.python.org/library/subprocess.html#subprocess.Popen.communicate

p.stdout.read(num_bytes) no le ayudará:

Advertencia Uso communicate() en lugar de .stdin.write, .stdout.read o .stderr.read para evitar los puntos muertos debido a cualquiera de los otros tampones de tubería OS llenar y bloqueando el proceso del niño.

http://docs.python.org/library/subprocess.html#subprocess.Popen.stdout

La moraleja de la historia es, para la salida grande, subprocess.PIPE le doom a un fracaso seguro si el programa está tratando de leer los datos (me parece que debe ser capaz de poner p.stdout.read(bytes) en un bucle while p.returncode is None:, pero la advertencia anterior sugiere que esto podría bloquearse).

Los documentos sugieren la sustitución de un tubo de cáscara con esto:

p1 = Popen(["zgrep", "thingiwant", "largefile"], stdout=PIPE) 
p2 = Popen(["processreceivingdata"], stdin=p1.stdout, stdout=PIPE) 
output = p2.communicate()[0] 

en cuenta que p2 está tomando su entrada estándar directamente de p1. Este debe evitar interbloqueos, pero dadas las advertencias contradictorias anteriores, que sabe.

De todos modos, si esa última parte no funciona para usted (debería, sin embargo), podría intentar crear un archivo temporal, escribiendo todos los datos de la primera llamada y luego usar el archivo temporal como entrada al siguiente proceso.

0

¿Cómo ejecutó este proceso?

manera adecuada es utilizar

p.communicate() 

Ver documentos para más detalles.

+0

Esto ocurre incluso si nunca me comunico con el proceso. Solo crear el objeto p y luego salir del intérprete causa este problema. –

+0

Sí, si recuerdo correctamente, Popen ejecuta el comando. 'communicate()' luego espera hasta que el proceso haya finalizado, los búferes se vacían, etc. etc. También vea 'check_call()'. – Almad

2

Después de abrir el tubo, se puede trabajar con la salida del comando: p.stdout:

for line in p.stdout: 
    # do stuff 
p.stdout.close() 
0

Es necesario wait para el proceso de terminar:

import subprocess 
p = subprocess.Popen("cat /mach_kernel", shell=True) 
p.wait() 

Como alternativa, puede capturar la salida estándar del programa (como usted tiene), y tal vez su error estándar, y luego llame al communicate:

import subprocess 
p = subprocess.Popen("cat /mach_kernel", shell=True, 
        stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
stdout, stderr = p.communicate()