variable tamaño stringvar logaritmica fijar etiquetas escala ejes cambiar python subprocess stdout stderr tee

tamaño - ¿Cómo replicar el comportamiento de tee en Python cuando se usa un subproceso?



stringvar tkinter python 3 (7)

Escribí una cosa que envuelve los comandos de shell en Python.

Principales ventajas:

  1. Este util captura siempre stdout / stderr
  2. Esta utilidad proporciona una opción para repetir stdout / stderr a stdout / stderr para el proceso
  3. Cuando se hace eco de stdout / stderr out / err no hay retraso

Clave desventaja:

  • Solo funciona en bash / unix

fuente: https://gist.github.com/AndrewHoos/9f03c74988469b517a7a

Estoy buscando una solución Python que me permita guardar la salida de un comando en un archivo sin ocultarlo de la consola.

FYI: estoy preguntando acerca de tee (como la utilidad de línea de comando de Unix) y no la función con el mismo nombre del módulo de herramientas de Python

Detalles

  • Solución Python (no llama a tee , no está disponible en Windows)
  • No necesito proporcionar ninguna entrada a stdin para el proceso llamado
  • No tengo control sobre el programa llamado. Todo lo que sé es que dará salida a stdout y stderr y regresará con un código de salida.
  • Para trabajar al llamar a programas externos (subproceso)
  • Trabajar tanto para stderr como para stdout
  • Poder diferenciar entre stdout y stderr porque es posible que desee mostrar solo uno de los a la consola o podría intentar generar stderr con un color diferente, lo que significa que stderr = subprocess.STDOUT no funcionará.
  • Salida en vivo (progresiva): el proceso puede ejecutarse durante mucho tiempo y no puedo esperar a que finalice.
  • Código compatible con Python 3 (importante)

Referencias

Aquí hay algunas soluciones incompletas que encontré hasta ahora:

Diagrama http://blog.i18n.ro/wp-content/uploads/2010/06/Drawing_tee_py.png

Código actual (segundo intento)

#!/usr/bin/python from __future__ import print_function import sys, os, time, subprocess, io, threading cmd = "python -E test_output.py" from threading import Thread class StreamThread ( Thread ): def __init__(self, buffer): Thread.__init__(self) self.buffer = buffer def run ( self ): while 1: line = self.buffer.readline() print(line,end="") sys.stdout.flush() if line == '''': break proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdoutThread = StreamThread(io.TextIOWrapper(proc.stdout)) stderrThread = StreamThread(io.TextIOWrapper(proc.stderr)) stdoutThread.start() stderrThread.start() proc.communicate() stdoutThread.join() stderrThread.join() print("--done--") #### test_output.py #### #!/usr/bin/python from __future__ import print_function import sys, os, time for i in range(0, 10): if i%2: print("stderr %s" % i, file=sys.stderr) else: print("stdout %s" % i, file=sys.stdout) time.sleep(0.1) Salida real

stderr 1 stdout 0 stderr 3 stdout 2 stderr 5 stdout 4 stderr 7 stdout 6 stderr 9 stdout 8 --done--

La salida esperada era tener las líneas ordenadas. Nota: no está permitido modificar el Popen para usar solo un PIPE porque en la vida real querré hacer cosas diferentes con stderr y stdout.

Además, incluso en el segundo caso no pude obtener en tiempo real, de hecho, todos los resultados se recibieron cuando finalizó el proceso. Por defecto, Popen no debe usar buffers (bufsize = 0).


Este es un puerto directo de tee a Python.

import sys sinks = sys.argv[1:] sinks = [open(sink, "w") for sink in sinks] sinks.append(sys.stderr) while True: input = sys.stdin.read(1024) if input: for sink in sinks: sink.write(input) else: break

Estoy ejecutando Linux ahora mismo, pero esto debería funcionar en la mayoría de las plataformas.

Ahora, para la parte del subprocess , no sé cómo desea "conectar" los stdin , stdout y stderr del subproceso a sus stdin , stdout , stderr y archivos de receptores, pero sé que puede hacer esto:

import subprocess callee = subprocess.Popen( ["python", "-i"], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE )

Ahora puede acceder a los archivos normales callee.stdin , callee.stdout y callee.stderr , permitiendo que la "solución" anterior funcione. Si desea obtener el callee.returncode , deberá realizar una llamada adicional a callee.poll() .

Tenga cuidado al escribir a callee.stdin : si el proceso ha finalizado al hacerlo, puede haber un error (en Linux, me sale el error IOError: [Errno 32] Broken pipe ).


Finalmente tuve que implementar el comando tee() en Python.

Puede obtenerlo aquí http://github.com/pycontribs/tendo/blob/master/tendo/tee.py

Actualmente te permite hacer cosas como:

tee("python --v") # works just like os.system() tee("python --v", "log.txt") # file names tee("python --v", file_handle) import logging tee("python --v", logging.info) # receive a method

La única limitación actual es que no es capaz de diferenciar entre stderr y stdout , lo que significa que fusionará ambos.


Hay problemas / errores sutiles en Python relacionados con el subproceso. PIPE: http://bugs.python.org/issue1652

Aparentemente, esto se solucionó en python3 +, pero no en python 2.7 y anteriores. Para eso necesitas usar: code.google.com/p/python-subprocess32/


Prueba esto :

import sys class tee-function : def __init__(self, _var1, _var2) : self.var1 = _var1 self.var2 = _var2 def __del__(self) : if self.var1 != sys.stdout and self.var1 != sys.stderr : self.var1.close() if self.var2 != sys.stdout and self.var2 != sys.stderr : self.var2.close() def write(self, text) : self.var1.write(text) self.var2.write(text) def flush(self) : self.var1.flush() self.var2.flush() stderrsav = sys.stderr out = open(log, "w") sys.stderr = tee-function(stderrsav, out)


Si no desea interactuar con el proceso, puede utilizar el módulo de subproceso sin problemas.

Ejemplo:

tester.py

import os import sys for file in os.listdir(''.''): print file sys.stderr.write("Oh noes, a shrubbery!") sys.stderr.flush() sys.stderr.close()

testing.py

import subprocess p = subprocess.Popen([''python'', ''tester.py''], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() print stdout, stderr

En su situación, puede escribir primero stdout / stderr en un archivo. También puede enviar argumentos a su proceso para comunicarse, aunque no pude averiguar cómo interactuar continuamente con el subproceso.


Veo que esta es una publicación bastante antigua, pero en caso de que alguien todavía esté buscando una manera de hacer esto:

proc = subprocess.Popen(["ping", "localhost"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) with open("logfile.txt", "w") as log_file: while proc.poll() is None: line = proc.stderr.readline() if line: print "err: " + line.strip() log_file.write(line) line = proc.stdout.readline() if line: print "out: " + line.strip() log_file.write(line)