Comment obtenir l'environnement d'un sous-processus?
-
06-07-2019 - |
Question
Je souhaite appeler un processus via un programme python. Toutefois, ce processus nécessite certaines variables d'environnement spécifiques définies par un autre processus. Comment puis-je faire en sorte que les premières variables d’environnement de processus les transmettent à la seconde?
Voici à quoi ressemble le programme:
import subprocess
subprocess.call(['proc1']) # this set env. variables for proc2
subprocess.call(['proc2']) # this must have env. variables set by proc1 to work
mais le processus à traiter ne partage pas le même environnement. Notez que ces programmes ne sont pas les miens (le premier est un fichier .bat volumineux et laid, et le second est un logiciel propriétaire). Je ne peux donc pas les modifier (d'accord, je peux extraire tout ce dont j'ai besoin du .bat, mais c'est très complexe. ).
NB: J'utilise Windows, mais je préfère une solution multiplate-forme (mais mon problème ne se produirait pas sous Unix-like ...)
La solution
Puisque vous êtes apparemment sous Windows, vous avez besoin d'une réponse Windows.
Créez un fichier de commandes wrapper, par exemple. "run_program.bat", et exécutez les deux programmes:
@echo off
call proc1.bat
proc2
Le script s'exécutera et définira ses variables d'environnement. Les deux scripts s'exécutant dans le même interpréteur (instance cmd.exe), les variables prog1.bat seront donc définies lors de l'exécution de prog2.
Pas très beau, mais ça va marcher.
(Peuple Unix, vous pouvez faire la même chose dans un script bash: "source file.sh".)
Autres conseils
Voici un exemple montrant comment extraire des variables d’environnement d’un fichier batch ou cmd sans créer de script wrapper. Profitez.
from __future__ import print_function
import sys
import subprocess
import itertools
def validate_pair(ob):
try:
if not (len(ob) == 2):
print("Unexpected result:", ob, file=sys.stderr)
raise ValueError
except:
return False
return True
def consume(iter):
try:
while True: next(iter)
except StopIteration:
pass
def get_environment_from_batch_command(env_cmd, initial=None):
"""
Take a command (either a single command or list of arguments)
and return the environment created after running that command.
Note that if the command must be a batch file or .cmd file, or the
changes to the environment will not be captured.
If initial is supplied, it is used as the initial environment passed
to the child process.
"""
if not isinstance(env_cmd, (list, tuple)):
env_cmd = [env_cmd]
# construct the command that will alter the environment
env_cmd = subprocess.list2cmdline(env_cmd)
# create a tag so we can tell in the output when the proc is done
tag = 'Done running command'
# construct a cmd.exe command to do accomplish this
cmd = 'cmd.exe /s /c "{env_cmd} && echo "{tag}" && set"'.format(**vars())
# launch the process
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=initial)
# parse the output sent to stdout
lines = proc.stdout
# consume whatever output occurs until the tag is reached
consume(itertools.takewhile(lambda l: tag not in l, lines))
# define a way to handle each KEY=VALUE line
handle_line = lambda l: l.rstrip().split('=',1)
# parse key/values into pairs
pairs = map(handle_line, lines)
# make sure the pairs are valid
valid_pairs = filter(validate_pair, pairs)
# construct a dictionary of the pairs
result = dict(valid_pairs)
# let the process finish
proc.communicate()
return result
Donc, pour répondre à votre question, vous devez créer un fichier .py qui effectue les opérations suivantes:
env = get_environment_from_batch_command('proc1')
subprocess.Popen('proc2', env=env)
Comme vous le dites, les processus ne partagent pas l'environnement. Par conséquent, ce que vous demandez littéralement n'est pas possible, pas seulement en Python, mais avec n'importe quel langage de programmation.
Ce que vous pouvez faire, c'est placer les variables d'environnement dans un fichier ou dans un tube, puis
.- demandez au processus parent de les lire et transmettez-les à proc2 avant la création de proc2, ou
- demandez à proc2 de les lire et de les définir localement
Ce dernier nécessiterait la coopération de proc2; le premier nécessite que les variables soient connues avant le démarrage de proc2.
Deux choses me viennent à l’esprit: (1) faire en sorte que les processus partagent le même environnement, en les combinant d’une manière ou d’une autre dans le même processus, ou (2) que le premier processus produise une sortie contenant les variables d’environnement pertinentes, afin que Python puisse lisez-le et construisez l'environnement pour le second processus. Je pense (même si je ne suis pas tout à fait sûr) qu'il n'y a aucun moyen d'obtenir l'environnement d'un sous-processus comme vous espérez le faire.
Le multitraitement du module standard Python possède un système de files d'attente qui vous permet de passer en mode pickle. objet -able à passer à travers les processus. Les processus peuvent également échanger des messages (un objet décapé) à l'aide de os.pipe. N'oubliez pas que les ressources (par exemple, la connexion à une base de données) et le descripteur (par exemple, les descripteurs de fichier) ne peuvent pas être traités.
Vous pouvez trouver ce lien intéressant: Communication entre processus avec multitraitement
Le PyMOTw sur le multitraitement mérite également d’être mentionné: Principes de base du multitraitement
désolé pour mon orthographe
L'environnement est hérité du processus parent. Définissez l'environnement dont vous avez besoin dans le script principal, et non un sous-processus (enfant).