Фактическое значение 'shell = True' в подпроцессе - PullRequest
209 голосов
/ 03 июля 2010

Я вызываю разные процессы с помощью модуля subprocess.Однако у меня есть вопрос.

В следующих кодах:

callProcess = subprocess.Popen(['ls', '-l'], shell=True)

и

callProcess = subprocess.Popen(['ls', '-l']) # without shell

Оба работают.Прочитав документы, я узнал, что shell=True означает выполнение кода через оболочку.Таким образом, это означает, что в отсутствие, процесс запускается напрямую.

Итак, что я предпочитаю для своего случая - мне нужно запустить процесс и получить его вывод.Какую выгоду я получаю от вызова внутри или вне оболочки.

Ответы [ 5 ]

148 голосов
/ 03 июля 2010

Преимущество отказа от вызова через оболочку состоит в том, что вы не вызываете «загадочную программу». В POSIX переменная окружения SHELL определяет, какой двоичный файл вызывается как «оболочка». В Windows нет потомка оболочки Bourne, только cmd.exe.

Таким образом, вызов оболочки вызывает программу по выбору пользователя и зависит от платформы. Вообще говоря, избегайте вызовов через оболочку.

Вызов через оболочку позволяет расширять переменные среды и файловые глобусы в соответствии с обычным механизмом оболочки. В системах POSIX оболочка расширяет глобусы файлов до списка файлов. В Windows глобус файла (например, "*. *") В любом случае не раскрывается оболочкой (но переменные среды в командной строке расширяются с помощью cmd.exe).

Если вы считаете, что вам нужны расширения переменных среды и глобусы файлов, исследуйте атаки ILS 1992-го года на сетевые службы, которые выполняли вызовы подпрограмм через оболочку. Примеры включают в себя различные sendmail бэкдоры, включающие ILS.

В итоге используйте shell=False.

86 голосов
/ 30 марта 2016
>>> import subprocess
>>> subprocess.call('echo $HOME')
Traceback (most recent call last):
...
OSError: [Errno 2] No such file or directory
>>>
>>> subprocess.call('echo $HOME', shell=True)
/user/khong
0

Установка истинного значения аргумента оболочки приводит к тому, что подпроцесс порождает промежуточный процесс оболочки и говорит ему выполнить команду. Другими словами, использование промежуточной оболочки означает, что переменные, шаблоны глобусов и другие специальные функции оболочки в командной строке обрабатываются до запуска команды. Здесь, в примере, $ HOME был обработан перед командой echo. Фактически, это случай команды с расширением оболочки, тогда как команда ls -l рассматривается как простая команда.

источник: Модуль подпроцесса

35 голосов
/ 03 июля 2010

Выполнение программ через оболочку означает, что весь пользовательский ввод, переданный программе, интерпретируется в соответствии с синтаксическими и семантическими правилами вызванной оболочки.В лучшем случае это только доставляет неудобства пользователю, потому что пользователь должен соблюдать эти правила.Например, пути, содержащие специальные символы оболочки, такие как кавычки или пробелы, должны быть экранированы.В худшем случае это приводит к утечкам в системе безопасности, поскольку пользователь может выполнять произвольные программы.

shell=True иногда удобно использовать специальные функции оболочки, такие как разделение слов или расширение параметров.Однако, если такая функция требуется, вам будут предоставлены другие модули (например, os.path.expandvars() для расширения параметров или shlex для разделения слов).Это означает больше работы, но позволяет избежать других проблем.

Короче: избегайте shell=True всеми средствами.

32 голосов
/ 13 марта 2015

Пример, где все может пойти не так с Shell = True, показан здесь

>>> from subprocess import call
>>> filename = input("What file would you like to display?\n")
What file would you like to display?
non_existent; rm -rf / # THIS WILL DELETE EVERYTHING IN ROOT PARTITION!!!
>>> call("cat " + filename, shell=True) # Uh-oh. This will end badly...

Проверьте документ здесь: subprocess.call ()

15 голосов
/ 15 марта 2016

Другие ответы здесь адекватно объясняют предостережения безопасности, которые также упоминаются в документации subprocess. Но в дополнение к этому, накладные расходы на запуск оболочки для запуска программы, которую вы хотите запустить, часто не нужны и, безусловно, глупы в ситуациях, когда вы фактически не используете какую-либо функциональность оболочки. Более того, дополнительная скрытая сложность должна вас напугать, , особенно , если вы не очень хорошо знакомы с оболочкой или предоставляемыми ей услугами.

Расширение с подстановочными знаками, интерполяция переменных и перенаправление - все просто заменить на собственные конструкции Python. Сложный конвейер оболочки, в котором части или все не могут быть разумно переписаны в Python (специализированные внешние инструменты, возможно, с закрытым исходным кодом?), Был бы единственной ситуацией, когда вы могли бы рассмотреть возможность использования оболочки. Вы все еще должны чувствовать себя плохо из-за этого.

В обычном случае просто замените

subprocess.Popen("command -with -options 'like this' and\\ an\\ argument", shell=True)

с

subprocess.Popen(['command', '-with','-options', 'like this', 'and an argument'])

Обратите внимание, что первый аргумент - это список строк для передачи в execvp(), и как кавычки строк и метасимволы оболочки с обратной косой чертой обычно не нужны (или полезны, или корректны).

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

  • При check=True произойдет сбой, если команда, которую вы запустили, не удалась.
  • С stdout=subprocess.PIPE он будет захватывать вывод команды.
  • Несколько неясно, с universal_newlines=True он будет декодировать вывод в правильную строку Unicode (это просто bytes в кодировке системы, в противном случае на Python 3).

Если нет, для многих задач вы хотите, чтобы check_output получал выходные данные команды, проверяя ее успешность, или check_call, если выходных данных нет собирать.

В заключение я приведу цитату из Дэвида Корна: «Легче написать переносную оболочку, чем сценарий переносимой оболочки». Даже subprocess.run('echo "$HOME"', shell=True) не переносим для Windows.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...