web-dev-qa-db-fra.com

python sous-processus environnement Popen CHEMIN?

Je suis confus quant à la façon dont subprocess recherche l'exécutable lors de l'utilisation de Popen(). Cela fonctionne si on leur donne des chemins absolus vers le processus enfant, mais j'essaie d'utiliser des chemins relatifs. J'ai constaté que si je définissais la variable d'environnement PYTHONPATH, je pourrais obtenir des modules importés à partir de ce chemin ok, et PYTHONPATH est là dans sys.path, mais cela ne semble pas aider le comportement de subprocess.Popen. J'ai également essayé de modifier le sitecustomize.py fichier ajoutant PYTHONPATH à os.environ, ainsi

# copy PYTHONPATH environment variable into PATH to allow our stuff to use
# relative paths for subprocess spawning
import os
if os.getenv('PYTHONPATH') is not None and os.getenv('PATH') is not none:
    os.environ['PATH'] = ':'.join([os.getenv('PATH'), os.getenv('PYTHONPATH')])

et vérifié qu'au démarrage de python, soit de manière interactive, avec ipython, soit en exécutant un script à partir de la ligne de commande, PYTHONPATH apparaît avec succès dans os.environ. Toutefois, subrocess.Popentoujours n'y recherche pas l'exécutable. Je pensais qu'il était censé hériter de l'environnement des parents, si aucun env kwarg n'est spécifié? Ensuite, j'ai essayé de donner explicitement env, d'abord en faisant une copie de os.getenv et deuxièmement simplement en donnant env={'PATH': '/explicit/path/to/search/from'}, et il ne trouve toujours pas l'exécutable. Maintenant, je suis perplexe.

J'espère qu'un exemple aidera à expliquer mon problème plus clairement:

/ dir/subdir1/some_executable
/dir/subdir2/some_script.py

# some_script.py
from subprocess import Popen, PIPE
spam, eggs = Popen(['../subdir1/some_executable'], stdout=PIPE, stderr=PIPE).communicate()

Si je suis dans /dir/subdir2 et je lance python some_script.py ça marche, mais si je suis dans /dir et je lance python subdir2/some_script.py même si /dir/subdir2 est dans le os.environ['PATH'], le sous-processus lancera OSError: [Errno 2] No such file or directory.

52
wim

(en remplissant les détails d'un commentaire pour faire une réponse séparée)

Tout d'abord, les chemins relatifs (chemins contenant des barres obliques) ne sont jamais vérifiés dans aucun CHEMIN, quoi que vous fassiez. Ils sont relatifs au répertoire de travail actuel uniquement. Si vous devez résoudre des chemins relatifs, vous devrez rechercher le CHEMIN manuellement ou munir le CHEMIN pour inclure les sous-répertoires, puis utiliser simplement le nom de la commande comme dans ma suggestion ci-dessous.

Si vous souhaitez exécuter un programme par rapport à l'emplacement du script Python , utilisez __file__ et allez à partir de là pour trouver le chemin absolu du programme, puis utilisez le chemin absolu dans Popen.

Deuxièmement, il y a n problème dans le Python bug tracker sur la façon dont Python traite les commandes nues (pas de barres obliques). Fondamentalement, sous Unix/Mac Popen utilise os.execvp lorsqu'il est appelé avec Shell=False, ce qui signifie qu'il regarde la valeur de PATH tel qu'il était lorsque Python lancé et aucune modification de os.environ ne vous aidera à résoudre ce problème. De plus, sous Windows avec Shell=False, il ne prête aucune attention à PATH et ne regardera que par rapport au répertoire de travail actuel.

Si vous avez JUSTEMENT besoin d'une évaluation de chemin et que vous ne voulez pas vraiment exécuter votre ligne de commande via un shell, et que vous êtes sous UNIX, je vous conseille d'utiliser env au lieu de Shell=True, Comme dans Popen(['/usr/bin/env', 'progtorun', other, args], ...). Cela vous permet de passer un PATH différent au processus env, qui l'utilisera pour trouver le programme. Cela évite également les problèmes de métacaractères Shell et les problèmes de sécurité potentiels liés au passage d'arguments via le Shell. Évidemment, sous Windows (à peu près la seule plate-forme sans /usr/bin/env), Vous devrez faire quelque chose de différent.

58
Walter Mundt

Vous semblez un peu confus quant à la nature de PATH et PYTHONPATH.

PATH est une variable d'environnement qui indique au shell du système d'exploitation où rechercher les exécutables.

PYTHONPATH est une variable d'environnement qui indique à l'interpréteur Python où chercher les modules à importer. Cela n'a rien à voir avec subprocess pour trouver des fichiers exécutables.

En raison des différences dans l'implémentation sous-jacente, subprocess.Popen ne recherchera le chemin par défaut que sur les systèmes non Windows (Windows possède certains répertoires système qu'il recherche toujours, mais cela est différent du traitement PATH). Le seul moyen fiable multiplateforme pour analyser le chemin consiste à passer Shell=True à l'appel du sous-processus, mais qui a ses propres problèmes (comme détaillé dans la documentation Popen )

Cependant, il semble que votre problème principal soit que vous transmettez un fragment de chemin à Popen plutôt qu'un simple nom de fichier. Dès que vous aurez un séparateur de répertoires, vous allez désactiver la recherche PATH, même sur une plate-forme non Windows (par exemple, voir la documentation Linux pour la famille de fonctions exec ).

12
ncoghlan

Un chemin relatif dans subprocess.Popen agit par rapport au répertoire de travail actuel, et non aux éléments du système PATH. Si vous exécutez python subdir2/some_script.py de /dir que l'emplacement exécutable attendu sera /dir/../subdir2/some_executable, alias /subdir2/some_executable.

Si vous souhaitez vraiment utiliser des chemins relatifs depuis un répertoire de scripts propre vers un exécutable particulier, la meilleure option serait de construire d'abord un chemin absolu à partir de la partie répertoire du __file__ variable globale.

#/usr/bin/env python
from subprocess import Popen, PIPE
from os.path import abspath, dirname, join
path = abspath(join(dirname(__file__), '../subdir1/some_executable'))
spam, eggs = Popen(path, stdout=PIPE, stderr=PIPE).communicate()
2
Jeremy Fishman

Le chemin python est défini sur le chemin à partir duquel l'interpréteur python est exécuté. Donc, dans le deuxième cas de votre exemple, le chemin est défini sur/dir et non/dir/subdir2 C'est pourquoi vous obtenez une erreur.

0
c0da