Python подпроцесс Popen environment PATH?
Я смущен тем, как subprocess
выполняет поиск исполняемого файла при использовании Popen()
. Он работает, если заданы абсолютные пути к дочернему процессу, но я пытаюсь использовать относительные пути. Я обнаружил, что если я установил переменную окружения PYTHONPATH, тогда я смогу импортировать модули из этого пути в порядке, а PYTHONPATH находится в sys.path
, но, похоже, это не помогает в поведении subprocess.Popen
. Я также попытался отредактировать файл sitecustomize.py
, добавив PYTHONPATH в os.environ
, например
# 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')])
и проверил, что при запуске python, либо в интерактивном режиме, с помощью ipython, либо путем запуска script из командной строки, что PYTHONPATH успешно появляется в os.environ
. Однако subrocess.Popen
по-прежнему не выполняет поиск для исполняемого файла. Я думал, что он должен наследовать родительскую среду, если не указано env
kwarg? Затем я попытался дать env
явно, сначала сделав копию os.getenv
, а во-вторых, просто нажав env={'PATH': '/explicit/path/to/search/from'}
, и он все еще не находит исполняемый файл. Теперь я в тупике.
Надеюсь, что пример поможет более четко объяснить мою проблему:
/реж/subdir1/some_executable
/dir/subdir 2/some_script.py
# some_script.py
from subprocess import Popen, PIPE
spam, eggs = Popen(['../subdir1/some_executable'], stdout=PIPE, stderr=PIPE).communicate()
Если я в /dir/subdir2
и я запускаю python some_script.py
, он работает, но если я в /dir
, и я запускаю python subdir2/some_script.py
, хотя /dir/subdir2
находится в os.environ['PATH']
, тогда подпроцесс будет бросить OSError: [Errno 2] No such file or directory
.
Ответы
Ответ 1
(заполняя детали из комментария, чтобы сделать отдельный ответ)
Во-первых, относительные пути (пути, содержащие косые черты) никогда не проверяются ни в какой PATH, независимо от того, что вы делаете. Они относятся только к текущему рабочему каталогу. Если вам нужно разрешить относительные пути, вам придется вручную искать PATH или переместить PATH, чтобы включить подкаталоги, а затем просто используйте имя команды, как в моем предложении ниже.
Если вы хотите запустить программу относительно местоположения Python script, используйте __file__
и идите оттуда, чтобы найти абсолютный путь программы, а затем используйте абсолютный путь в Popen
.
Во-вторых, существует проблема в Python tuger о том, как Python имеет дело с голыми командами (без косой черты). В основном, в Unix/Mac Popen
используется os.execvp
при вызове с shell=False
, что означает, что он смотрит на значение PATH
, как это было при запуске Python, и никакое изменение os.environ
не поможет вам исправить это, Кроме того, в Windows с shell=False
он вообще не обращает внимания на PATH и будет смотреть только относительно текущего рабочего каталога.
Если вам просто нужна оценка пути и вы действительно не хотите запускать свою командную строку через оболочку и находитесь в UNIX, я советую использовать env
вместо shell=True
, как в Popen(['/usr/bin/env', 'progtorun', other, args], ...)
. Это позволяет передать другой PATH в процесс env
, который будет использовать его для поиска программы. Он также избегает проблем с метасимволами оболочки и потенциальными проблемами безопасности с передачей аргументов через оболочку. Очевидно, что в Windows (в значительной степени единственная платформа без /usr/bin/env
) вам нужно будет сделать что-то другое.
Ответ 2
Похоже, вы немного смущены характером PATH
и PYTHONPATH
.
PATH
- это переменная среды, которая сообщает оболочке ОС, где искать исполняемые файлы.
PYTHONPATH
- это переменная среды, которая сообщает интерпретатору Python, где искать модули для импорта. Он не имеет ничего общего с subprocess
для поиска исполняемых файлов.
Из-за различий в базовой реализации, subprocess.Popen
будет искать путь по умолчанию только в системах, отличных от Windows (Windows имеет некоторые системные каталоги, которые он всегда ищет, но отличается от обработки PATH
). Единственный надежный кросс-платформенный способ сканирования пути - передать shell=True
на вызов подпроцесса, но у него есть свои проблемы (как описано в Popen
документация)
Однако, похоже, ваша основная проблема заключается в том, что вы передаете фрагмент пути Popen
вместо простого имени файла. Как только у вас есть разделитель каталогов, вы отключите поиск PATH
даже на платформе, отличной от Windows (например, см. Документацию по Linux для exec функции).
Ответ 3
Относительный путь в подпроцессе. Действие действует относительно текущего рабочего каталога, а не элементов систем PATH. Если вы запустите python subdir2/some_script.py
из /dir
, чем ожидаемое исполняемое местоположение будет /dir/../subdir2/some_executable
, a.k.a /subdir2/some_executable
.
Если вы определенно хотели бы использовать относительные пути из собственного каталога скриптов в конкретный исполняемый файл, лучшим вариантом было бы сначала построить абсолютный путь из части каталога глобальной переменной __file__
.
#/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()
Ответ 4
Путь python устанавливается в путь, из которого выполняется интерпретатор python. Итак, во втором случае вашего примера путь устанавливается в /dir, а не /dir/subdir 2
Вот почему вы получаете сообщение об ошибке.