Question

Je dois exécuter une commande shell de manière asynchrone à partir d'un script Python. J'entends par là que je veux que mon script Python continue à s'exécuter pendant que la commande externe se déclenche et fait tout ce dont elle a besoin.

J'ai lu ce post:

  

Appel d'une commande externe en Python

Je suis ensuite parti et j'ai fait quelques tests. Il semble que os.system () effectue le travail à condition que j'utilise & amp; à la fin de la commande afin que je ne dois pas attendre son retour. Ce que je me demande, c'est si c'est la bonne façon d'accomplir une telle chose? J'ai essayé orders.call () mais cela ne fonctionnera pas car cela bloque la commande externe.

Faites-moi savoir si l'utilisation de os.system () est souhaitable ou si je devrais essayer une autre route.

Était-ce utile?

La solution

sous-processus.Popen fait exactement ce que vous voulez.

from subprocess import Popen
p = Popen(['watch', 'ls']) # something long running
# ... do other stuff while subprocess is running
p.terminate()

(Modifier pour compléter la réponse à partir des commentaires)

L’instance Popen peut faire d’autres choses comme vous pouvez poll () pour voir s'il est toujours en cours d'exécution et vous pouvez communiquer () avec lui pour lui envoyer des données sur stdin et attendre sa fin.

Autres conseils

Si vous souhaitez exécuter plusieurs processus en parallèle, puis les gérer lorsqu'ils génèrent des résultats, vous pouvez utiliser l'interrogation comme suit:

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)

Le flux de contrôle est un peu compliqué parce que j'essaie de le rendre petit - vous pouvez le refactoriser à votre goût. : -)

Cela présente l'avantage de traiter d'abord les demandes de finition anticipée. Si vous appelez communicez lors du premier processus en cours d'exécution et qu'il s'avère que l'exécution est la plus longue, l'autre les processus en cours seront restés inactifs alors que vous auriez pu gérer leurs résultats.

Ce que je me demande, c'est si ce [os.system ()] est la bonne façon d'accomplir une telle chose?

Non. os.system () n’est pas le bon moyen. C'est pourquoi tout le monde dit d'utiliser le sous-processus .

Pour plus d'informations, consultez http://docs.python.org/library /os.html#os.system

  

Le module de sous-processus fournit plus   installations puissantes pour le frai de nouveaux   processus et la récupération de leur   résultats; en utilisant ce module est   préférable à l’utilisation de cette fonction. Utilisation   le module de sous-processus. Vérifier   en particulier le remplacement des anciens   Fonctions avec le module de sous-processus   section.

J'ai eu un bon succès avec le module asyncproc . , qui traite bien du résultat des processus. Par exemple:

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

Utilisation de pexpect [ http://www.noah.org/wiki/Pexpect ] avec Des lignes de lecture non bloquantes sont un autre moyen de le faire. Pexpect résout les problèmes d'interblocage, vous permet d'exécuter facilement les processus en arrière-plan et offre des moyens simples d'obtenir des rappels lorsque votre processus génère des chaînes prédéfinies et facilite généralement beaucoup plus les interactions avec le processus.

J'ai le même problème lors de la tentative de connexion à un terminal 3270 à l'aide du logiciel de script s3270 de Python. Maintenant, je résous le problème avec une sous-classe de Processus que j'ai trouvée ici:

http://code.activestate.com/recipes/440554/

Et voici l'exemple extrait du fichier:

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()

Considérant que "je n’ai pas besoin d’attendre son retour", l’une des solutions les plus simples sera la suivante:

subprocess.Popen( \
    [path_to_executable, arg1, arg2, ... argN],
    creationflags = subprocess.CREATE_NEW_CONSOLE,
).pid

Mais ... D'après ce que j'ai lu, ce n'est pas "la manière appropriée d'accomplir une telle chose". en raison des risques de sécurité créés par l’indicateur subprocess.CREATE_NEW_CONSOLE .

Ce qui est important ici, c’est l’utilisation du sous-processus.CREATE_NEW_CONSOLE pour créer une nouvelle console et du .pid (retourne l’ID du processus afin que vous puissiez vérifier le programme ultérieurement si vous le souhaitez. vouloir) pour ne pas attendre que le programme termine son travail.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top