esta - ejecutar python desde cmd
Imprime constantemente la salida del subproceso mientras el proceso se está ejecutando (7)
@tokland
intenté tu código y lo corrigí para 3.4 y windows dir.cmd es un simple comando dir, guardado como archivo cmd
import subprocess
c = "dir.cmd"
def execute(command):
popen = subprocess.Popen(command, stdout=subprocess.PIPE,bufsize=1)
lines_iterator = iter(popen.stdout.readline, b"")
while popen.poll() is None:
for line in lines_iterator:
nline = line.rstrip()
print(nline.decode("latin"), end = "/r/n",flush =True) # yield line
execute(c)
Para lanzar programas desde mi Python-scripts, 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 Process.execute("mvn clean install")
un proceso como Process.execute("mvn clean install")
, mi programa espera hasta que el proceso finalice, y solo entonces obtengo el resultado completo de mi programa. Esto es molesto si estoy ejecutando un proceso que toma un tiempo para terminar.
¿Puedo permitir que mi programa escriba la salida del proceso línea por línea, sondeando la salida del proceso antes de que termine en un bucle o algo así?
** [EDIT] 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
En caso de que alguien quiera leer de stdout
y stderr
al mismo tiempo, usando hilos, esto es lo que se me ocurrió:
import threading
import subprocess
import Queue
class AsyncLineReader(threading.Thread):
def __init__(self, fd, outputQueue):
threading.Thread.__init__(self)
assert isinstance(outputQueue, Queue.Queue)
assert callable(fd.readline)
self.fd = fd
self.outputQueue = outputQueue
def run(self):
map(self.outputQueue.put, iter(self.fd.readline, ''''))
def eof(self):
return not self.is_alive() and self.outputQueue.empty()
@classmethod
def getForFd(cls, fd, start=True):
queue = Queue.Queue()
reader = cls(fd, queue)
if start:
reader.start()
return reader, queue
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdoutReader, stdoutQueue) = AsyncLineReader.getForFd(process.stdout)
(stderrReader, stderrQueue) = AsyncLineReader.getForFd(process.stderr)
# Keep checking queues until there is no more output.
while not stdoutReader.eof() or not stderrReader.eof():
# Process all available lines from the stdout Queue.
while not stdoutQueue.empty():
line = stdoutQueue.get()
print ''Received stdout: '' + repr(line)
# Do stuff with stdout line.
# Process all available lines from the stderr Queue.
while not stderrQueue.empty():
line = stderrQueue.get()
print ''Received stderr: '' + repr(line)
# Do stuff with stderr line.
# Sleep for a short time to avoid excessive CPU use while waiting for data.
sleep(0.05)
print "Waiting for async readers to finish..."
stdoutReader.join()
stderrReader.join()
# Close subprocess'' file descriptors.
process.stdout.close()
process.stderr.close()
print "Waiting for process to exit..."
returnCode = process.wait()
if returnCode != 0:
raise subprocess.CalledProcessError(returnCode, command)
Solo quería compartir esto, ya que terminé con esta pregunta tratando de hacer algo similar, pero ninguna de las respuestas resolvió mi problema. ¡Espero que ayude a alguien!
Tenga en cuenta que en mi caso de uso, un proceso externo mata el proceso que Popen()
.
Este PoC lee constantemente el resultado de un proceso y se puede acceder cuando sea necesario. Solo se conserva el último resultado, el resto de la salida se descarta, por lo tanto, evita que el PIPE crezca fuera de la memoria:
import subprocess
import time
import threading
import Queue
class FlushPipe(object):
def __init__(self):
self.command = [''python'', ''./print_date.py'']
self.process = None
self.process_output = Queue.LifoQueue(0)
self.capture_output = threading.Thread(target=self.output_reader)
def output_reader(self):
for line in iter(self.process.stdout.readline, b''''):
self.process_output.put_nowait(line)
def start_process(self):
self.process = subprocess.Popen(self.command,
stdout=subprocess.PIPE)
self.capture_output.start()
def get_output_for_processing(self):
line = self.process_output.get()
print ">>>" + line
if __name__ == "__main__":
flush_pipe = FlushPipe()
flush_pipe.start_process()
now = time.time()
while time.time() - now < 10:
flush_pipe.get_output_for_processing()
time.sleep(2.5)
flush_pipe.capture_output.join(timeout=0.001)
flush_pipe.process.kill()
print_date.py
#!/usr/bin/env python
import time
if __name__ == "__main__":
while True:
print str(time.time())
time.sleep(0.01)
salida: se puede ver claramente que solo hay salida del intervalo de ~ 2,5 s nada en el medio.
>>>1520535158.51
>>>1520535161.01
>>>1520535163.51
>>>1520535166.01
Ok, logré resolverlo sin hilos (se aprecia cualquier sugerencia de por qué usar hilos sería mejor) usando un fragmento de esta pregunta. Interceptando la salida estándar de un subproceso mientras se está ejecutando.
def execute(command):
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Poll process for new output until finished
while True:
nextline = process.stdout.readline()
if nextline == '''' and process.poll() is not None:
break
sys.stdout.write(nextline)
sys.stdout.flush()
output = process.communicate()[0]
exitCode = process.returncode
if (exitCode == 0):
return output
else:
raise ProcessException(command, exitCode, output)
Para cualquiera que intente las respuestas a esta pregunta para obtener el stdout de un script de Python, tenga en cuenta que Python almacena temporalmente su stdout y, por lo tanto, puede llevar un tiempo ver el stdout.
Esto puede rectificarse agregando lo siguiente después de cada escritura de stdout en el script de destino:
sys.stdout.flush()
Para imprimir el subproceso ''salida línea por línea tan pronto como se vacía su búfer stdout en Python 3:
from subprocess import Popen, PIPE, CalledProcessError
with Popen(cmd, stdout=PIPE, bufsize=1, universal_newlines=True) as p:
for line in p.stdout:
print(line, end='''') # process line here
if p.returncode != 0:
raise CalledProcessError(p.returncode, p.args)
Aviso: no necesita p.poll()
- el ciclo finaliza cuando se alcanza eof. Y no necesita iter(p.stdout.readline, '''')
- el error de lectura anticipada está arreglado en Python 3.
Ver también, Python: leer la entrada de transmisión desde subprocess.communicate () .
Puede usar iter para procesar líneas tan pronto como el comando las lines = iter(fd.readline, "")
: lines = iter(fd.readline, "")
. Aquí hay un ejemplo completo que muestra un caso de uso típico (gracias a @JF Sebastian por ayudar):
from __future__ import print_function # Only Python 2.x
import subprocess
def execute(cmd):
popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True)
for stdout_line in iter(popen.stdout.readline, ""):
yield stdout_line
popen.stdout.close()
return_code = popen.wait()
if return_code:
raise subprocess.CalledProcessError(return_code, cmd)
# Example
for path in execute(["locate", "a"]):
print(path, end="")