Фактический смысл "shell = True" в подпроцессе

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

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

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

и

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

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

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

Ответ 1

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

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

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

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

В заключение используйте shell=False.

Ответ 2

>>> 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

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

source: Модуль подпроцесса

Ответ 3

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

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

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

Ответ 4

Пример, где все может пойти не так, как показано в 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()

Ответ 5

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

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

В тривиальном случае, чтобы избежать shell=True, просто замените

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, или check_call если нет выходных данных для сбора.

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