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.Popen
toujours 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
.
(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.
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 ).
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()
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.