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:

  

Llamar a un comando externo en Python

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.

¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top