Подтвердить что ты не робот

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.

4b9b3361

Ответ 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 Вот почему вы получаете сообщение об ошибке.