¿Cómo puedo ejecutar un comando externo de forma asíncrona desde Python?
-
10-07-2019 - |
Pregunta
Necesito ejecutar un comando de shell de forma asíncrona desde un script de Python. Con esto quiero decir que quiero que mi script Python continúe ejecutándose mientras el comando externo se apaga y hace lo que sea necesario.
Leí esta publicación:
Luego me fui e hice algunas pruebas, y parece que os.system ()
hará el trabajo siempre que use & amp;
al final del comando para que no tenga que esperar a que regrese. Lo que me pregunto es si esta es la forma correcta de lograr tal cosa. Intenté command.call ()
pero no me funcionará porque bloquea el comando externo.
Avíseme si usar os.system ()
para esto es aconsejable o si debería intentar alguna otra ruta.
Solución
subprocess.Popen hace exactamente lo que quiere.
from subprocess import Popen
p = Popen(['watch', 'ls']) # something long running
# ... do other stuff while subprocess is running
p.terminate()
(Editar para completar la respuesta de los comentarios)
La instancia de Popen puede hacer otras cosas como usted puede sondee ()
para ver si todavía se está ejecutando, y puede comunicar ()
con él para enviarle datos en stdin y esperar a que termine.
Otros consejos
Si desea ejecutar muchos procesos en paralelo y luego manejarlos cuando producen resultados, puede usar el sondeo de la siguiente manera:
from subprocess import Popen, PIPE
import time
running_procs = [
Popen(['/usr/bin/my_cmd', '-i %s' % path], stdout=PIPE, stderr=PIPE)
for path in '/tmp/file0 /tmp/file1 /tmp/file2'.split()]
while running_procs:
for proc in running_procs:
retcode = proc.poll()
if retcode is not None: # Process finished.
running_procs.remove(proc)
break
else: # No process is done, wait a bit and check again.
time.sleep(.1)
continue
# Here, `proc` has finished with return code `retcode`
if retcode != 0:
"""Error handling."""
handle_results(proc.stdout)
El flujo de control allí es un poco complicado porque estoy tratando de hacerlo pequeño: puede refactorizarlo a su gusto. :-)
Esto tiene la ventaja de atender primero las solicitudes de finalización temprana. Si llama a comunicar
en el primer proceso en ejecución y resulta ser el más largo, el otro los procesos en ejecución habrán estado inactivos cuando podría haber estado manejando sus resultados.
¿Lo que me pregunto es si este [os.system ()] es la forma correcta de lograr tal cosa?
No. os.system ()
no es la forma correcta. Es por eso que todo el mundo dice que use subprocess
.
Para obtener más información, lea http://docs.python.org/library /os.html#os.system
El módulo de subproceso proporciona más poderosas instalaciones para desovar nuevas procesos y recuperar sus resultados; usando ese módulo es preferible a usar esta función. Utilizar El módulo de subproceso. Comprobar especialmente el reemplazo de mayores Funciones con el módulo de subproceso sección.
He tenido buen éxito con el asyncproc módulo , que trata muy bien con la salida de los procesos. Por ejemplo:
import os
from asynproc import Process
myProc = Process("myprogram.app")
while True:
# check to see if process has ended
poll = myProc.wait(os.WNOHANG)
if poll is not None:
break
# print any new output
out = myProc.read()
if out != "":
print out
Uso de pexpect [ http://www.noah.org/wiki/Pexpect ] con Las líneas de lectura sin bloqueo son otra forma de hacerlo. Pexpect resuelve los problemas de punto muerto, le permite ejecutar fácilmente los procesos en segundo plano y ofrece formas fáciles de tener devoluciones de llamada cuando su proceso escupe cadenas predefinidas, y generalmente hace que la interacción con el proceso sea mucho más fácil.
Tengo el mismo problema al tratar de conectarme a un terminal 3270 usando el software de scripting s3270 en Python. Ahora estoy resolviendo el problema con una subclase de Proceso que encontré aquí:
http://code.activestate.com/recipes/440554/
Y aquí está la muestra tomada del archivo:
def recv_some(p, t=.1, e=1, tr=5, stderr=0):
if tr < 1:
tr = 1
x = time.time()+t
y = []
r = ''
pr = p.recv
if stderr:
pr = p.recv_err
while time.time() < x or r:
r = pr()
if r is None:
if e:
raise Exception(message)
else:
break
elif r:
y.append(r)
else:
time.sleep(max((x-time.time())/tr, 0))
return ''.join(y)
def send_all(p, data):
while len(data):
sent = p.send(data)
if sent is None:
raise Exception(message)
data = buffer(data, sent)
if __name__ == '__main__':
if sys.platform == 'win32':
shell, commands, tail = ('cmd', ('dir /w', 'echo HELLO WORLD'), '\r\n')
else:
shell, commands, tail = ('sh', ('ls', 'echo HELLO WORLD'), '\n')
a = Popen(shell, stdin=PIPE, stdout=PIPE)
print recv_some(a),
for cmd in commands:
send_all(a, cmd + tail)
print recv_some(a),
send_all(a, 'exit' + tail)
print recv_some(a, e=0)
a.wait()
Considerando "no tengo que esperar a que vuelva", una de las soluciones más fáciles será esta:
subprocess.Popen( \
[path_to_executable, arg1, arg2, ... argN],
creationflags = subprocess.CREATE_NEW_CONSOLE,
).pid
Pero ... Por lo que leí, esto no es "la forma correcta de lograr tal cosa". debido a los riesgos de seguridad creados por subprocess.CREATE_NEW_CONSOLE
flag.
Las cosas clave que suceden aquí es el uso del subproceso .CREATE_NEW_CONSOLE
para crear una nueva consola y .pid
(devuelve la identificación del proceso para que pueda verificar el programa más adelante si desea) para no esperar a que el programa termine su trabajo.