web-dev-qa-db-fra.com

Comment démarrer un processus en arrière-plan en Python?

J'essaie de porter un script Shell dans la version beaucoup plus lisible de python. Le script Shell d'origine démarre plusieurs processus (utilitaires, moniteurs, etc.) en arrière-plan avec "&". Comment puis-je obtenir le même effet en python? J'aimerais que ces processus ne meurent pas lorsque les scripts python seront terminés. Je suis sûr que cela est lié au concept de démon, mais je ne trouvais pas comment le faire facilement.

247
Artem

Remarque : Cette réponse est moins actuelle qu'elle ne l'était en 2009. Il est maintenant recommandé d'utiliser le module subprocess présenté dans d'autres réponses - dans la documentation

(Notez que le module de sous-processus offre des fonctionnalités plus puissantes pour générer de nouveaux processus et extraire leurs résultats. Il est préférable d’utiliser ce module plutôt que d’utiliser ces fonctions.)


Si vous souhaitez que votre processus commence en arrière-plan, vous pouvez utiliser system() et l'appeler de la même manière que votre script Shell ou vous pouvez spawn:

import os
os.spawnl(os.P_DETACH, 'some_long_running_command')

(ou vous pouvez également essayer le drapeau moins portable os.P_NOWAIT _).

Voir le documentation ici .

72
jkp

Bien que la solution de jkp fonctionne, la méthode la plus récente (et la méthode recommandée par la documentation) consiste à utiliser le module subprocess. Son équivalent pour les commandes simples, mais il offre plus d'options si vous voulez faire quelque chose de compliqué.

Exemple pour votre cas:

import subprocess
subprocess.Popen(["rm","-r","some.file"])

Cela devrait exécuter rm -r somefile en arrière-plan. Mais méfiez-vous: subprocess.Popen() n'exécute un processus en arrière-plan que si rien dans le script python ne dépend du résultat de la commande en cours d'exécution:

Par exemple, la commande suivante ne s'exécutera pas en arrière-plan:

import subprocess
ls_output=subprocess.Popen(["ls", "-a"], stdout=subprocess.PIPE)

Voir la documentation ici .

Également, un éclaircissement: "Contexte", un concept purement Shell: vous voulez probablement créer un nouveau processus. J'ai utilisé ici "background" pour désigner un comportement similaire à Shell, mais ne le confondez pas avec le processus en arrière-plan.

331
Dan

Vous voulez probablement la réponse à "Comment appeler une commande externe en Python" .

L’approche la plus simple consiste à utiliser la fonction os.system, par exemple:

import os
os.system("some_command &")

Fondamentalement, tout ce que vous transmettez à la fonction system sera exécuté de la même manière que si vous l'aviez transmise au shell dans un script.

39
Eli Courtwright

J'ai trouvé ceci ici :

Sous Windows (win xp), le processus parent ne sera pas terminé tant que le longtask.py n'aura pas terminé son travail. Ce n'est pas ce que vous voulez dans CGI-script. Le problème n'est pas spécifique à Python, dans PHP communauté, les problèmes sont les mêmes.

La solution consiste à passer DETACHED_PROCESSindicateur de création de processus à la fonction CreateProcess sous-jacente de Win API. Si vous avez installé pywin32, vous pouvez importer l’indicateur à partir du module win32process, sinon vous devez le définir vous-même:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid
28
f p

Utilisez subprocess.Popen() avec le paramètre close_fds=True, ce qui permettra au sous-processus engendré d'être détaché du processus Python et de s'exécuter même après la sortie de Python.

https://Gist.github.com/yinjimmy/d6ad0742d03d54518e9f

import os, time, sys, subprocess

if len(sys.argv) == 2:
    time.sleep(5)
    print 'track end'
    if sys.platform == 'darwin':
        subprocess.Popen(['say', 'hello'])
else:
    print 'main begin'
    subprocess.Popen(['python', os.path.realpath(__file__), '0'], close_fds=True)
    print 'main end'
20
Jimmy Yin

Vous voudrez probablement commencer à examiner le module os pour trouver différents threads (en ouvrant une session interactive et en émettant une aide (os)). Les fonctions pertinentes sont fork et n’importe lequel d’exec. Pour vous donner une idée de la façon de commencer, insérez quelque chose comme ceci dans une fonction effectuant le fork (la fonction doit utiliser une liste ou un Tuple 'args' comme argument contenant le nom du programme et ses paramètres; vous pouvez également vouloir pour définir stdin, out et err pour le nouveau thread):

try:
    pid = os.fork()
except OSError, e:
    ## some debug output
    sys.exit(1)
if pid == 0:
    ## eventually use os.putenv(..) to set environment variables
    ## os.execv strips of args[0] for the arguments
    os.execv(args[0], args)

Capturer la sortie et s'exécuter en arrière-plan avec threading

Comme mentionné sur cette réponse , si vous capturez la sortie avec stdout= puis essayez de read(), le processus se bloque.

Cependant, il y a des cas où vous en avez besoin. Par exemple, je voulais lancer deux processus qui parlent via un port entre eux , et enregistrer leur sortie standard dans un fichier journal et la sortie standard.

Le module threading nous permet de le faire.

Tout d’abord, regardez comment faire la redirection de sortie seule dans cette question: Python Popen: écrire simultanément dans stdout ET le fichier journal

Ensuite:

main.py

#!/usr/bin/env python3

import os
import subprocess
import sys
import threading

def output_reader(proc, file):
    while True:
        byte = proc.stdout.read(1)
        if byte:
            sys.stdout.buffer.write(byte)
            sys.stdout.flush()
            file.buffer.write(byte)
        else:
            break

with subprocess.Popen(['./sleep.py', '0'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc1, \
     subprocess.Popen(['./sleep.py', '10'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc2, \
     open('log1.log', 'w') as file1, \
     open('log2.log', 'w') as file2:
    t1 = threading.Thread(target=output_reader, args=(proc1, file1))
    t2 = threading.Thread(target=output_reader, args=(proc2, file2))
    t1.start()
    t2.start()
    t1.join()
    t2.join()

sommeil.py

#!/usr/bin/env python3

import sys
import time

for i in range(4):
    print(i + int(sys.argv[1]))
    sys.stdout.flush()
    time.sleep(0.5)

Après avoir couru:

./main.py

stdout est mis à jour toutes les 0.5 secondes pour que chaque ligne contienne:

0
10
1
11
2
12
3
13

et chaque fichier journal contient le journal correspondant pour un processus donné.

Inspiré par: https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/

Testé sur Ubuntu 18.04, Python 3.6.7.