(заполнение деталей из комментария, чтобы дать отдельный ответ)
Во-первых, относительные пути (пути, содержащие косую черту) никогда не проверяются ни в каком PATH
, что бы вы ни делали. Они относятся только к текущему рабочему каталогу. Если вам нужно разрешить относительные пути, вам придется искать PATH
вручную или изменить PATH
, чтобы включить подкаталоги, а затем просто использовать имя команды, как в моем предложении ниже.
Если вы хотите запустить программу относительно местоположения скрипта Python, используйте __file__
и перейдите оттуда, чтобы найти абсолютный путь к программе, а затем используйте абсолютный путь в Popen
.
Поиск в переменной среды текущего процесса PATH
Во-вторых, существует проблема в системе отслеживания ошибок Python о том, как Python работает с голыми командами (без косой черты). Обычно в Unix / Mac Popen
ведет себя как os.execvp
, когда аргумент env=None
(было замечено и отмечено в конце неожиданное поведение):
В POSIX класс использует os.execvp()
-подобное поведение для выполнения дочерней программы.
На самом деле это верно как для shell=False
, так и для shell=True
при условии env=None
. Что означает это поведение, объясняется в документации функции os.execvp
. :
Варианты, которые содержат букву «p» в конце (execlp()
, execlpe()
, execvp()
и execvpe()
), будут использовать переменную среды PATH
для поиска файла программы. Когда среда заменяется (с использованием одного из вариантов exec*e
, обсуждаемых в следующем абзаце), новая среда используется в качестве источника переменной PATH
.
Для execle()
, execlpe()
, execve()
и execvpe()
(обратите внимание, что все они заканчиваются на «e») параметр env должен быть отображением, которое используется для определения переменных среды для нового процесса (это используется вместо среды текущего процесса); все функции execl()
, execlp()
, execv()
и execvp()
заставляют новый процесс наследовать среду текущего процесса.
Второй цитируемый абзац подразумевает, что execvp
будет использовать переменные среды текущего процесса. В сочетании с первым процитированным абзацем мы делаем вывод, что execvp
будет использовать значение переменной среды PATH
из среды текущего процесса. Это означает, что Popen
смотрит на значение PATH
, как это было при запуске Python (Python, который запускает Popen
экземпляр), и никакие изменения os.environ
не помогут вам это исправить.
Кроме того, в Windows с shell=False
Popen
вообще не обращает внимания на PATH
и будет искать только относительно текущего рабочего каталога.
Что делает shell=True
Что произойдет, если мы передадим shell=True
в Popen
? В этом случае Popen
просто вызывает оболочку:
Аргумент shell (по умолчанию False
) указывает, следует ли использовать оболочку в качестве выполняемой программы.
То есть Popen
выполняет эквивалент:
Popen(['/bin/sh', '-c', args[0], args[1], ...])
Другими словами, с shell=True
Python будет напрямую выполнять /bin/sh
, без какого-либо поиска (передача аргумента executable
в Popen
может изменить это, и кажется, что если это строка без косой черты, тогда она будет интерпретироваться Python как имя программы оболочки для поиска в значении PATH
из среды текущего процесса, т. е. как он ищет программы в случае shell=False
, описанном выше).
В свою очередь, /bin/sh
(или наша оболочка executable
) будет искать программу, которую мы хотим запустить, в PATH
своей собственной среды, которая совпадает с PATH
Python (текущий процесс), как следует из кода после фразы That is чтобы сказать ... выше (потому что этот вызов имеет shell=False
, так что это уже обсуждалось ранее). Следовательно, поведение, подобное execvp
, мы получаем как с shell=True
, так и с shell=False
, пока env=None
.
Передача env
в Popen
Итак, что произойдет, если мы передадим env=dict(PATH=...)
в Popen
(тем самым определив переменную среды PATH
в среде программы, которая будет запускаться Popen
)?
В этом случае новая среда используется для поиска программы для выполнения. Цитата из документации Popen
:
Если env не None
, это должно быть отображение, которое определяет переменные среды для нового процесса; они используются вместо поведения по умолчанию при наследовании среды текущего процесса.
В сочетании с приведенными выше наблюдениями и экспериментами с использованием Popen
это означает, что Popen
в этом случае ведет себя как функция _ 72_. Если shell=False
, Python ищет данную программу во вновь определенном PATH
. Как уже обсуждалось выше для shell=True
, в этом случае программа либо /bin/sh
, либо, если имя программы задано с аргументом executable
, тогда эта альтернативная программа (оболочка) ищется во вновь определенном PATH
.
Вдобавок, если shell=True
, то внутри оболочки путь поиска, который оболочка будет использовать для поиска программы, указанной в args
, является значением PATH
, переданным в Popen
через env
.
Таким образом, с env != None
, Popen
ищет значение ключа PATH
из env
(если ключ PATH
присутствует в env
).
Распространение переменных среды, отличных от PATH
, в качестве аргументов
Существует предостережение относительно переменных среды, отличных от PATH
: если значения этих переменных необходимы в команде (например, в качестве аргументов командной строки для выполняемой программы), то даже если они присутствуют в env
, заданном для Popen
, они не будут интерпретированы без shell=True
. Этого легко избежать, не изменяя shell=True
: вставьте это значение непосредственно в list
аргумент args
, который передается в Popen
. (Кроме того, если эти значения поступают из собственной среды Python, для получения их значений можно использовать метод os.environ.get
).
Использование /usr/bin/env
Если вам ТОЛЬКО нужна оценка пути, и вы действительно не хотите запускать командную строку через оболочку и используете UNIX, я советую использовать env
вместо shell=True
, как в
path = '/dir1:/dir2'
subprocess.Popen(['/usr/bin/env', '-P', path, 'progtorun', other, args], ...)
Это позволяет передать другой PATH
процессу env
(с помощью параметра -P
), который будет использовать это найти программу. Это также позволяет избежать проблем с метасимволами оболочки и потенциальных проблем безопасности при передаче аргументов через оболочку. Очевидно, что в Windows (практически единственной платформе без /usr/bin/env
) вам нужно будет сделать что-то другое.
О shell=True
Цитата из Popen
документации:
Если shell - True
, рекомендуется передавать args
как строку, а не как последовательность.
Примечание. Ознакомьтесь с соображениями безопасности. раздел перед использованием shell=True
.
Неожиданные наблюдения
Наблюдалось следующее поведение:
Этот колл вызывает FileNotFoundError
, как и ожидалось:
subprocess.call(['sh'], shell=False, env=dict(PATH=''))
Этот вызов обнаруживает sh
, что является неожиданным:
subprocess.call(['sh'], shell=False, env=dict(FOO=''))
Ввод echo $PATH
внутри открывающейся оболочки показывает, что значение PATH
не пусто, а также отличается от значения PATH
в среде Python. Таким образом, кажется, что PATH
действительно не был унаследован от Python (как и ожидалось в присутствии env != None
), но, тем не менее, PATH
непусто. Неизвестно, почему это так.
Этот колл вызывает FileNotFoundError
, как и ожидалось:
subprocess.call(['tree'], shell=False, env=dict(FOO=''))
Это обнаруживает tree
, как и ожидалось:
subprocess.call(['tree'], shell=False, env=None)
person
Walter Mundt
schedule
14.04.2011
/dir
и посмотрите, что произойдет, если вы наберете../subdir1/some_executable
. - person ncoghlan   schedule 14.04.2011