python subprocess stdout

python - Mostrar salida de subproceso a stdout y redirigirlo



subprocess (3)

El documento de Popen.communicate establece claramente:

Note: The data read is buffered in memory, so do not use this method if the data size is large or unlimited.

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

Entonces, si necesita una salida en tiempo real, debe usar algo como esto:

stream_p = subprocess.Popen(''/path/to/script'', stdout=subprocess.PIPE, stderr=subprocess.PIPE) while stream_line in stream_p: #Parse it the way you want print stream_line

Estoy ejecutando un script a través del módulo de subproceso de Python. Actualmente uso:

p = subprocess.Popen(''/path/to/script'', stdout=subprocess.PIPE, stderr=subprocess.PIPE) result = p.communicate()

Luego imprimo el resultado en el stdout. Todo esto está bien, pero como el script tarda mucho tiempo en completarse, también quería que la salida del script en tiempo real también fuera estándar. La razón por la que canalizo la salida es porque quiero analizarla.


Para guardar el stdout del subproceso en una variable para su posterior procesamiento y mostrarlo mientras el proceso secundario se está ejecutando a medida que llega :

#!/usr/bin/env python3 from io import StringIO from subprocess import Popen, PIPE with Popen(''/path/to/script'', stdout=PIPE, bufsize=1, universal_newlines=True) as p, StringIO() as buf: for line in p.stdout: print(line, end='''') buf.write(line) output = buf.getvalue() rc = p.returncode

Para guardar tanto el subproceso stdout como el stderr es más complejo porque debe consumir ambos flujos simultáneamente para evitar un punto muerto :

stdout_buf, stderr_buf = StringIO(), StringIO() rc = teed_call(''/path/to/script'', stdout=stdout_buf, stderr=stderr_buf, universal_newlines=True) output = stdout_buf.getvalue() ...

donde teed_call() se define aquí .

Actualización: aquí hay una versión asyncio más asyncio .

Versión antigua:

Aquí hay una solución de subproceso único basada en el ejemplo child_process.py de tulip :

import asyncio import sys from asyncio.subprocess import PIPE @asyncio.coroutine def read_and_display(*cmd): """Read cmd''s stdout, stderr while displaying them as they arrive.""" # start process process = yield from asyncio.create_subprocess_exec(*cmd, stdout=PIPE, stderr=PIPE) # read child''s stdout/stderr concurrently stdout, stderr = [], [] # stderr, stdout buffers tasks = { asyncio.Task(process.stdout.readline()): ( stdout, process.stdout, sys.stdout.buffer), asyncio.Task(process.stderr.readline()): ( stderr, process.stderr, sys.stderr.buffer)} while tasks: done, pending = yield from asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) assert done for future in done: buf, stream, display = tasks.pop(future) line = future.result() if line: # not EOF buf.append(line) # save for later display.write(line) # display in terminal # schedule to read the next line tasks[asyncio.Task(stream.readline())] = buf, stream, display # wait for the process to exit rc = yield from process.wait() return rc, b''''.join(stdout), b''''.join(stderr)

El script ejecuta ''/path/to/script comando ''/path/to/script y lee línea por línea tanto su stdout como stderr simultáneamente. Las líneas se imprimen en stdout / stderr principal de forma correspondiente y se guardan como cadenas de bytes para su procesamiento futuro. Para ejecutar la read_and_display() , necesitamos un bucle de eventos:

import os if os.name == ''nt'': loop = asyncio.ProactorEventLoop() # for subprocess'' pipes on Windows asyncio.set_event_loop(loop) else: loop = asyncio.get_event_loop() try: rc, *output = loop.run_until_complete(read_and_display("/path/to/script")) if rc: sys.exit("child failed with ''{}'' exit code".format(rc)) finally: loop.close()


p.communicate() espera a que se complete el subproceso y luego devuelve toda su salida a la vez.

¿Has probado algo como esto en el que lees la salida del subproceso línea por línea?

p = subprocess.Popen(''/path/to/script'', stdout=subprocess.PIPE, stderr=subprocess.PIPE) for line in p.stdout: # do something with this individual line print line