example español python subprocess

example - subprocess python español



Usando subprocess.Popen para proceso con salida grande (7)

Tengo un código Python que ejecuta una aplicación externa que funciona bien cuando la aplicación tiene una pequeña cantidad de resultados, pero se bloquea cuando hay mucho Mi código se ve como:

p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) errcode = p.wait() retval = p.stdout.read() errmess = p.stderr.read() if errcode: log.error(''cmd failed <%s>: %s'' % (errcode,errmess))

Hay comentarios en los documentos que parecen indicar el problema potencial. En espera, hay:

Advertencia: Esto se interrumpirá si el proceso hijo genera suficiente salida a un stdout o stderr pipe, de modo que bloquea la espera de que el buffer del pipe del sistema operativo acepte más datos. Utilice communicate() para evitar eso.

Aunque bajo comunicación, veo:

Nota La lectura de los datos se almacena en la memoria intermedia, por lo que no utilice este método si el tamaño de los datos es grande o ilimitado.

Por lo tanto, no me queda claro si debo usar cualquiera de estos si tengo una gran cantidad de datos. No indican qué método debo usar en ese caso.

Necesito el valor de retorno del exec y hago un análisis y uso tanto el stdout como el stderr .

Entonces, ¿cuál es un método equivalente en Python para ejecutar una aplicación externa que tendrá un gran rendimiento?


Estás haciendo lecturas de bloqueo a dos archivos; el primero debe completarse antes de que comience el segundo. Si la aplicación escribe mucho en stderr , y nada en stdout , entonces su proceso se sentará a la espera de los datos de stdout que no llegan, mientras que el programa que está ejecutando se queda allí esperando que se lean las cosas que escribió en stderr (lo que nunca será - ya que estás esperando stdout ).

Hay algunas maneras en que puedes arreglar esto.

Lo más simple es no interceptar stderr ; dejar stderr=None . Los errores serán enviados directamente a stderr . No puedes interceptarlos y mostrarlos como parte de tu propio mensaje. Para las herramientas de línea de comandos, esto a menudo está bien. Para otras aplicaciones, puede ser un problema.

Otro método simple es redirigir stderr a stdout , de modo que solo tenga un archivo entrante: set stderr=STDOUT . Esto significa que no puede distinguir la salida regular de la salida de error. Esto puede o no ser aceptable, dependiendo de cómo la aplicación escribe el resultado.

La forma completa y complicada de manejar esto es select ( http://docs.python.org/library/select.html ). Esto le permite leer de forma no bloqueante: obtiene datos cada vez que aparecen datos en stdout o stderr . Solo recomendaría esto si es realmente necesario. Esto probablemente no funciona en Windows.


Este es un enfoque simple que captura tanto la salida regular más la salida de error, todo dentro de Python, por lo que las limitaciones en la salida stdout no se aplican:

com_str = ''uname -a'' command = subprocess.Popen([com_str], stdout=subprocess.PIPE, shell=True) (output, error) = command.communicate() print output Linux 3.11.0-20-generic SMP Fri May 2 21:32:55 UTC 2014

y

com_str = ''id'' command = subprocess.Popen([com_str], stdout=subprocess.PIPE, shell=True) (output, error) = command.communicate() print output uid=1000(myname) gid=1000(mygrp) groups=1000(cell),0(root)


Glenn Maynard tiene razón en su comentario sobre los puntos muertos. Sin embargo, la mejor manera de resolver este problema es crear dos subprocesos, uno para stdout y otro para stderr, que lean esos flujos respectivos hasta que se agoten y hagan lo que sea necesario con la salida.

La sugerencia de usar archivos temporales puede o no funcionar para usted dependiendo del tamaño de la salida, etc. y si necesita procesar la salida del subproceso a medida que se genera.

Como Heikki Toivonen ha sugerido, debes mirar el método de communicate . Sin embargo, esto amortigua el stdout / stderr del subproceso en memoria y obtiene los devueltos de la llamada de communicate ; esto no es ideal para algunos escenarios. Pero la fuente del método de comunicación vale la pena mirar.

Otro ejemplo es en un paquete que mantengo, python-gnupg , donde el ejecutable gpg se genera a través del subprocess para hacer el trabajo pesado, y la envoltura de Python genera subprocesos para leer stdout y stderr de gpg y consumirlos como datos generados por gpg. También puede obtener algunas ideas mirando la fuente allí. Los datos producidos por gpg para stdout y stderr pueden ser bastante grandes, en el caso general.


Lectura de stdout y stderr independiente con una salida muy grande (es decir, muchos megabytes) utilizando select :

import subprocess, select proc = subprocess.Popen(cmd, bufsize=8192, shell=False, / stdout=subprocess.PIPE, stderr=subprocess.PIPE) with open(outpath, "wb") as outf: dataend = False while (proc.returncode is None) or (not dataend): proc.poll() dataend = False ready = select.select([proc.stdout, proc.stderr], [], [], 1.0) if proc.stderr in ready[0]: data = proc.stderr.read(1024) if len(data) > 0: handle_stderr_data(data) if proc.stdout in ready[0]: data = proc.stdout.read(1024) if len(data) == 0: # Read of zero bytes means EOF dataend = True else: outf.write(data)


Podrías intentar comunicarte y ver si eso resuelve tu problema. Si no, redirigiría la salida a un archivo temporal.


Yo tuve el mismo problema. Si tiene que manejar una salida grande, otra buena opción podría ser usar un archivo para stdout y stderr, y pasar esos archivos por parámetro.

Verifique el módulo tempfile en python: https://docs.python.org/2/library/tempfile.html .

Algo como esto podría funcionar

out = tempfile.NamedTemporaryFile(delete=False)

Entonces harías:

Popen(... stdout=out,...)

Luego puedes leer el archivo y borrarlo más tarde.


Una gran cantidad de salida es subjetiva, por lo que es un poco difícil hacer una recomendación. Si la cantidad de salida es realmente grande, es probable que no desee capturarlo todo con una sola llamada de lectura () de todos modos. Es posible que desee intentar escribir la salida en un archivo y luego extraer los datos de forma incremental, como por ejemplo:

f=file(''data.out'',''w'') p = subprocess.Popen(cmd, shell=True, stdout=f, stderr=subprocess.PIPE) errcode = p.wait() f.close() if errcode: errmess = p.stderr.read() log.error(''cmd failed <%s>: %s'' % (errcode,errmess)) for line in file(''data.out''): #do something