подпроцесс python Popen окружение PATH? - PullRequest
51 голосов
/ 14 апреля 2011

Я не совсем понимаю, как 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 или при запуске сценария из командной строки, PYTHONPATH успешно появляется в os.environ. Однако subrocess.Popen все еще не ищет там исполняемый файл. Я думал, что он должен был наследовать родительскую среду, если не указан env kwarg? Затем я попытался явно указать env, сначала сделав копию os.getenv, а затем просто указав env={'PATH': '/explicit/path/to/search/from'}, и он все еще не находит исполняемый файл. Теперь я в тупике.

Надеюсь, пример поможет объяснить мою проблему более четко:

/ реж / 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()

Если я в /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.

Ответы [ 4 ]

56 голосов
/ 14 апреля 2011

(заполняет детали из комментария, чтобы сделать отдельный ответ)

Во-первых, относительные пути (пути, содержащие косую черту) никогда не проверяются ни в одной переменной PATH, независимо от того, что вы делаете. Они относятся только к текущему рабочему каталогу . Если вам нужно разрешить относительные пути, вам придется искать в PATH вручную или искать в PATH, чтобы включить подкаталоги, а затем просто использовать имя команды, как в моем предложении ниже.

Если вы хотите запустить программу относительно местоположения скрипта Python , используйте __file__ и перейдите оттуда, чтобы найти абсолютный путь к программе, а затем используйте абсолютный путь в Popen.

Во-вторых, есть проблема в трекере ошибок Python о том, как 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], ...). Это позволяет передать другой путь в процесс env, который будет использовать его для поиска программы. Это также позволяет избежать проблем с метасимволами оболочки и потенциальных проблем безопасности при передаче аргументов через оболочку. Очевидно, что в Windows (практически единственная платформа без /usr/bin/env) вам нужно будет сделать что-то другое.

12 голосов
/ 14 апреля 2011

Вы, похоже, немного смущены природой PATH и PYTHONPATH.

PATH - переменной среды, которая сообщает оболочке ОС, где искать исполняемые файлы.

PYTHONPATH - это переменная окружения, которая сообщает интерпретатору Python, где искать модули для импорта.Это не имеет ничего общего с subprocess поиском исполняемых файлов.

Из-за различий в базовой реализации subprocess.Popen будет искать путь только по умолчанию в системах, отличных от Windows (Windows имеет некоторые системные каталогивсегда выполняет поиск, но это отличается от обработки PATH).Единственный надежный кроссплатформенный способ сканирования пути - это передача shell=True вызову подпроцесса, но у него есть свои проблемы (как подробно описано в Popen документации )

Однако, похоже, ваша основная проблема в том, что вы передаете фрагмент пути в Popen, а не в простое имя файла.Как только у вас будет разделитель каталогов, вы отключите поиск PATH даже на платформе, отличной от Windows (например, смотрите документацию по Linux для семейства функций exec ).

2 голосов
/ 14 апреля 2011

Относительный путь в подпроцессе.Popen действует относительно текущего рабочего каталога, а не элементов систем 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()
0 голосов
/ 14 апреля 2011

Pythonpath устанавливается на путь, из которого выполняется интерпретатор python.Итак, во втором случае вашего примера путь установлен в / dir, а не в / dir / subdir2. Вот почему вы получаете ошибку.

...