examples example español python python-3.x subprocess popen

python - example - Subproceso.Pobular: clonación de stdout y stderr tanto para el terminal como para las variables



subprocess python español (2)

¿Es posible modificar el código a continuación para tener una impresión de ''stdout'' y ''stderr''?

  • impreso en la terminal (en tiempo real),
  • y finalmente se almacena en las variables outs y errs ?

El código:

#!/usr/bin/python3 # -*- coding: utf-8 -*- import subprocess def run_cmd(command, cwd=None): p = subprocess.Popen(command, cwd=cwd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) outs, errs = p.communicate() rc = p.returncode outs = outs.decode(''utf-8'') errs = errs.decode(''utf-8'') return (rc, (outs, errs))

Gracias a @unutbu, gracias especiales para @ jf-sebastian, función final:

#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys from queue import Queue from subprocess import PIPE, Popen from threading import Thread def read_output(pipe, funcs): for line in iter(pipe.readline, b''''): for func in funcs: func(line.decode(''utf-8'')) pipe.close() def write_output(get): for line in iter(get, None): sys.stdout.write(line) def run_cmd(command, cwd=None, passthrough=True): outs, errs = None, None proc = Popen( command, cwd=cwd, shell=False, close_fds=True, stdout=PIPE, stderr=PIPE, bufsize=1 ) if passthrough: outs, errs = [], [] q = Queue() stdout_thread = Thread( target=read_output, args=(proc.stdout, [q.put, outs.append]) ) stderr_thread = Thread( target=read_output, args=(proc.stderr, [q.put, errs.append]) ) writer_thread = Thread( target=write_output, args=(q.get,) ) for t in (stdout_thread, stderr_thread, writer_thread): t.daemon = True t.start() proc.wait() for t in (stdout_thread, stderr_thread): t.join() q.put(None) outs = '' ''.join(outs) errs = '' ''.join(errs) else: outs, errs = proc.communicate() outs = '''' if outs == None else outs.decode(''utf-8'') errs = '''' if errs == None else errs.decode(''utf-8'') rc = proc.returncode return (rc, (outs, errs))


Para capturar y visualizar al mismo tiempo tanto stdout como stderr desde un proceso hijo línea por línea en un solo hilo, puede usar E / S asincrónicas:

#!/usr/bin/env python3 import asyncio import os import sys from asyncio.subprocess import PIPE @asyncio.coroutine def read_stream_and_display(stream, display): """Read from stream line by line until EOF, display, and capture the lines. """ output = [] while True: line = yield from stream.readline() if not line: break output.append(line) display(line) # assume it doesn''t block return b''''.join(output) @asyncio.coroutine def read_and_display(*cmd): """Capture cmd''s stdout, stderr while displaying them as they arrive (line by line). """ # start process process = yield from asyncio.create_subprocess_exec(*cmd, stdout=PIPE, stderr=PIPE) # read child''s stdout/stderr concurrently (capture and display) try: stdout, stderr = yield from asyncio.gather( read_stream_and_display(process.stdout, sys.stdout.buffer.write), read_stream_and_display(process.stderr, sys.stderr.buffer.write)) except Exception: process.kill() raise finally: # wait for the process to exit rc = yield from process.wait() return rc, stdout, stderr # run the event loop if os.name == ''nt'': loop = asyncio.ProactorEventLoop() # for subprocess'' pipes on Windows asyncio.set_event_loop(loop) else: loop = asyncio.get_event_loop() rc, *output = loop.run_until_complete(read_and_display(*cmd)) loop.close()


Puede engendrar hilos para leer los tubos stdout y stderr, escribir en una cola común y anexar a las listas. Luego use un tercer hilo para imprimir elementos de la cola.

import time import Queue import sys import threading import subprocess PIPE = subprocess.PIPE def read_output(pipe, funcs): for line in iter(pipe.readline, ''''): for func in funcs: func(line) # time.sleep(1) pipe.close() def write_output(get): for line in iter(get, None): sys.stdout.write(line) process = subprocess.Popen( [''random_print.py''], stdout=PIPE, stderr=PIPE, close_fds=True, bufsize=1) q = Queue.Queue() out, err = [], [] tout = threading.Thread( target=read_output, args=(process.stdout, [q.put, out.append])) terr = threading.Thread( target=read_output, args=(process.stderr, [q.put, err.append])) twrite = threading.Thread(target=write_output, args=(q.get,)) for t in (tout, terr, twrite): t.daemon = True t.start() process.wait() for t in (tout, terr): t.join() q.put(None) print(out) print(err)

La razón para usar el tercer subproceso, en lugar de dejar que los primeros dos subprocesos se impriman directamente en el terminal, es evitar que ambas declaraciones de impresión se produzcan al mismo tiempo, lo que puede dar lugar a un texto a veces confuso.

Lo anterior llama a random_print.py , que imprime a stdout y stderr al azar:

import sys import time import random for i in range(50): f = random.choice([sys.stdout,sys.stderr]) f.write(str(i)+''/n'') f.flush() time.sleep(0.1)

Esta solución toma prestados código e ideas de JF Sebastian, aquí .

Aquí hay una solución alternativa para sistemas tipo Unix, usando select.select :

import collections import select import fcntl import os import time import Queue import sys import threading import subprocess PIPE = subprocess.PIPE def make_async(fd): # https://.com/a/7730201/190597 ''''''add the O_NONBLOCK flag to a file descriptor'''''' fcntl.fcntl( fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK) def read_async(fd): # https://.com/a/7730201/190597 ''''''read some data from a file descriptor, ignoring EAGAIN errors'''''' # time.sleep(1) try: return fd.read() except IOError, e: if e.errno != errno.EAGAIN: raise e else: return '''' def write_output(fds, outmap): for fd in fds: line = read_async(fd) sys.stdout.write(line) outmap[fd.fileno()].append(line) process = subprocess.Popen( [''random_print.py''], stdout=PIPE, stderr=PIPE, close_fds=True) make_async(process.stdout) make_async(process.stderr) outmap = collections.defaultdict(list) while True: rlist, wlist, xlist = select.select([process.stdout, process.stderr], [], []) write_output(rlist, outmap) if process.poll() is not None: write_output([process.stdout, process.stderr], outmap) break fileno = {''stdout'': process.stdout.fileno(), ''stderr'': process.stderr.fileno()} print(outmap[fileno[''stdout'']]) print(outmap[fileno[''stderr'']])

Esta solución usa código e ideas de la publicación de Adam Rosenfield, aquí .