Para iniciar programas desde mis scripts Python, estoy usando el siguiente método:
def execute(command):
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = process.communicate()[0]
exitCode = process.returncode
if (exitCode == 0):
return output
else:
raise ProcessException(command, exitCode, output)
Entonces, cuando inicio un proceso como Process.execute("mvn clean install")
, mi programa espera hasta que finalice el proceso, y solo entonces obtengo la salida completa de mi programa. Esto es molesto si estoy ejecutando un proceso que tarda un tiempo en finalizar.
¿Puedo dejar que mi programa escriba la salida del proceso línea por línea, sondeando la salida del proceso antes de que finalice en un bucle o algo así?
** [EDITAR] Lo siento, no busqué muy bien antes de publicar esta pregunta. Enhebrar es en realidad la clave. Encontré un ejemplo aquí que muestra cómo hacerlo: ** Subproceso de Python. Abrir desde un hilo
python
subprocess
Ingo Fischer
fuente
fuente
Respuestas:
Puede utilizar iter para procesar líneas Tan pronto como las salidas mandase:
lines = iter(fd.readline, "")
. Aquí hay un ejemplo completo que muestra un caso de uso típico (gracias a @jfs por ayudarnos):fuente
for line in popen.stdout: print(line.decode(), end='')
. Para admitir Python 2 y 3, use bytes literales: de lob''
contrario,lines_iterator
nunca termina en Python 3.bufsize=1
(puede mejorar el rendimiento en Python 2), cerrar lapopen.stdout
tubería explícitamente (sin esperar a que la recolección de basura se encargue de ello) y subirsubprocess.CalledProcessError
(comocheck_call()
,check_output()
hacer). Laprint
declaración es diferente en Python 2 y 3: podría usar el hack de softspaceprint line,
(nota: coma) para evitar duplicar todas las nuevas líneas como lo hace su código y pasaruniversal_newlines=True
Python 3, para obtener texto en lugar de una respuesta relacionada con bytes .execute(["python", "-u", "child_thread.py"])
. Más información: stackoverflow.com/questions/14258500/…Ok, logré resolverlo sin hilos (cualquier sugerencia de por qué sería mejor usar hilos sería apreciada) usando un fragmento de esta pregunta Interceptando stdout de un subproceso mientras se está ejecutando
fuente
print line,
desys.stdout.write(nextline); sys.stdout.flush()
lo contrario, sería imprimir cada dos líneas Por otra parte, esto está utilizando la interfaz portátil de IPython, así que tal vez algo más estaba sucediendo -.. En cualquier caso, una llamada explícita aflush()
las obras.stderr=subprocess.STDOUT
astderr=subprocess.PIPE
y luego llamarprocess.stderr.readline()
desde dentro del bucle, me parece chocar con el punto muerto que se le advierte sobre la documentación para elsubprocess
módulo.stdout=subprocess.PIPE,stderr=subprocess.STDOUT
esto captura stderr, y creo (pero no lo he probado) que también captura stdin.Para imprimir la salida del subproceso línea por línea tan pronto como su búfer stdout se vacíe en Python 3:
Aviso: no es necesario
p.poll()
: el ciclo termina cuando se alcanza eof. Y no es necesarioiter(p.stdout.readline, '')
: el error de lectura anticipada se solucionó en Python 3.Vea también, Python: lea la entrada de transmisión de subprocess.communicate () .
fuente
sys.stdout.flush()
en el padre: stdout tiene un buffer de línea si no se redirige a un archivo / tubería y, por lo tanto, la impresiónline
vacía el búfer automáticamente. No es necesariosys.stdout.flush()
en el niño también: pase la-u
opción de línea de comandos en su lugar.>
, ejecutapython -u your-script.py > some-file
. Aviso:-u
opción que he mencionado anteriormente (no es necesario usarsys.stdout.flush()
).p.wait()
se llama a la salida delwith
bloque. Usop.returncode
.En realidad, hay una forma realmente simple de hacer esto cuando solo desea imprimir la salida:
Aquí simplemente estamos apuntando el subproceso a nuestra propia stdout, y usando la API de éxito o excepción existente.
fuente
shell=True
@tokland
probé su código y lo corregí para 3.4 y windows dir.cmd es un comando dir simple, guardado como archivo cmd
fuente
iter()
yend='\r\n'
son innecesarios Python usa el modo universal de líneas nuevas de forma predeterminada,'\n'
es decir, cualquiera se traduce'\r\n'
durante la impresión.'latin'
probablemente sea una codificación incorrecta, podría usarlauniversal_newlines=True
para obtener salida de texto en Python 3 (decodificada usando la codificación preferida de la configuración regional). No se detenga.poll()
, podría haber datos no leídos almacenados en el búfer. Si la secuencia de comandos de Python se ejecuta en una consola, su salida tiene un buffer de línea; puede forzar el almacenamiento en línea usando la-u
opción: no es necesarioflush=True
aquí.En caso de que alguien quiera leer de ambos
stdout
ystderr
al mismo tiempo usar hilos, esto es lo que se me ocurrió:Solo quería compartir esto, ya que terminé con esta pregunta tratando de hacer algo similar, pero ninguna de las respuestas resolvió mi problema. ¡Ojalá ayude a alguien!
Tenga en cuenta que en mi caso de uso, un proceso externo mata el proceso que nosotros
Popen()
.fuente
Para cualquiera que intente las respuestas a esta pregunta para obtener el stdout de una secuencia de comandos de Python, tenga en cuenta que Python amortigua su stdout y, por lo tanto, puede tomar un tiempo ver el stdout.
Esto se puede rectificar agregando lo siguiente después de cada escritura estándar en el script de destino:
fuente
import
el otro guión; investiguemultiprocessing
othreading
si necesita ejecución paralela.subprocess.run("/path/to/python/executable", "pythonProgramToRun.py")
En Python> = 3.5 usando
subprocess.run
funciona para mí:(obtener el resultado durante la ejecución también funciona sin
shell=True
) https://docs.python.org/3/library/subprocess.html#subprocess.runfuente
subprocess.run()
llamada solo regresa cuando el subproceso ha terminado de ejecutarse.>>> import subprocess; subprocess.run('top')
también parece imprimirse "durante la ejecución" (y la parte superior nunca termina). ¿Quizás no estoy captando alguna diferencia sutil?stdout=subprocess.PIPE
, solo puede leerla después detop
finalizar. Su programa Python se bloquea durante la ejecución del subproceso.run
método aún funciona si solo está interesado en ver la salida a medida que se genera. Si desea hacer algo con la salida en python de forma asincrónica, tiene razón en que no funciona.Para responder a la pregunta original, la mejor manera en que IMO es simplemente redirigiendo el subproceso
stdout
directamente a su programastdout
(opcionalmente, se puede hacer lo mismostderr
, como en el ejemplo a continuación)fuente
stdout
ystderr
hace lo mismo con menos código. Aunque supongo que explícito es mejor que implícito.Este PoC lee constantemente la salida de un proceso y se puede acceder cuando sea necesario. Solo se mantiene el último resultado, todos los demás resultados se descartan, por lo tanto, evita que el PIPE se quede sin memoria:
print_date.py
salida: puede ver claramente que solo hay una salida de ~ 2.5s de intervalo, nada en el medio.
fuente
Esto funciona al menos en Python3.4
fuente
Ninguna de las respuestas aquí abordó todas mis necesidades.
Un poco de historia: estoy usando un ThreadPoolExecutor para administrar un grupo de subprocesos, cada uno de los cuales ejecuta un subproceso y los ejecuta simultáneamente. (En Python2.7, pero esto también debería funcionar en la versión 3.x más reciente). No quiero usar subprocesos solo para la recopilación de salida, ya que quiero tantos disponibles como sea posible para otras cosas (un grupo de 20 procesos usaría 40 subprocesos solo para ejecutarse; 1 para el subproceso de proceso y 1 para stdout ... y más si quieres stderr supongo)
Estoy eliminando muchas excepciones y tal aquí, así que esto se basa en el código que funciona en la producción. Espero no haberlo arruinado en copiar y pegar. Además, ¡los comentarios son bienvenidos!
Estoy seguro de que aquí se agrega una sobrecarga, pero no es una preocupación en mi caso. Funcionalmente hace lo que necesito. Lo único que no he resuelto es por qué esto funciona perfectamente para los mensajes de registro, pero veo que algunos
print
mensajes aparecen más tarde y de una vez.fuente
En Python 3.6 usé esto:
fuente
subprocess.call()
tiene algunas verrugas que son reparadas por funciones más nuevas; en Python 3.6 generalmente lo usaríassubprocess.run()
para esto; Por conveniencia, la función de contenedor más antiguasubprocess.check_output()
también está disponible: devuelve la salida real del proceso (este código devolvería solo el código de salida, pero incluso imprimirá algo indefinido).