¿Cómo puedo ejecutar un comando externo de forma asíncrona desde Python?

Necesito ejecutar un comando de shell de forma asincrónica desde un script de Python. Con esto quiero decir que quiero que mi script de Python continúe ejecutándose mientras el comando externo se activa y hace lo que sea necesario.

Leí este post:

Llamando un comando externo en Python

Luego me fui e hice algunas pruebas, y parece que os.system() hará el trabajo siempre y cuando lo use & 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é commands.call() pero no funcionará para mí porque bloquea el comando externo.

Por favor, avíseme si es recomendable usar os.system() para esto o si debo probar alguna otra ruta.

subprocess.Popen hace exactamente lo que quieres.

 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 varias otras cosas, como usted puede poll() para ver si todavía se está ejecutando, y puede communicate() con ella para enviar datos en la entrada estándar, y esperar a que termine.

Si desea ejecutar muchos procesos en paralelo y luego manejarlos cuando producen resultados, puede usar el sondeo como se muestra a continuación:

 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 intentando que sea pequeño, usted puede refactorizarlo a su gusto. 🙂

Esto tiene la ventaja de atender primero las solicitudes de acabado temprano. Si llama a la communicate en el primer proceso en ejecución y resulta que la ejecución es la más larga, los otros procesos en ejecución estarán 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 todos dicen usar subprocess .

Para obtener más información, lea http://docs.python.org/library/os.html#os.system

El módulo de subproceso proporciona instalaciones más potentes para generar nuevos procesos y recuperar sus resultados; Usar ese módulo es preferible a usar esta función. Utilizar el módulo de subproceso. Verifique especialmente la sección Reemplazo de funciones antiguas con el subproceso Módulo.

He tenido un gran éxito con el módulo asyncproc , que trata muy bien con el resultado 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 

Usar pexpect [ http://www.noah.org/wiki/Pexpect ] con líneas de lectura no bloqueantes es otra forma de hacerlo. Pexpect resuelve los problemas de interlocking, le permite ejecutar fácilmente los procesos en segundo plano y brinda formas fáciles de realizar devoluciones de llamada cuando el proceso escupe cadenas predefinidas y, en general, facilita mucho más la interacción con el proceso.

Tengo el mismo problema al intentar conectarme a un terminal 3270 con 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() 

Teniendo en cuenta que “no tengo que esperar a que regrese”, 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 algo así” debido a los riesgos de seguridad creados por el indicador subprocess.CREATE_NEW_CONSOLE .

Las cosas clave que suceden aquí es el uso de subprocess.CREATE_NEW_CONSOLE para crear una nueva consola y .pid (devuelve la ID del proceso para que pueda verificar el progtwig más adelante si así lo desea) para que no espere a que el progtwig termine su trabajo.