it-roy-ru.com

Зачем использовать методы модуля Python os вместо непосредственного выполнения команд Shell?

Я пытаюсь понять, что является мотивацией использования библиотечных функций Python для выполнения специфических для ОС задач, таких как создание файлов/каталогов, изменение атрибутов файлов и т.д., Вместо простого выполнения этих команд с помощью os.system() или subprocess.call()?

Например, почему я хочу использовать os.chmod вместо os.system("chmod...")?

Я понимаю, что более "Pythonic" - использовать как можно больше доступных библиотечных методов Python вместо того, чтобы просто выполнять команды Shell напрямую. Но есть ли какая-то другая мотивация, делающая это с функциональной точки зрения?

Я говорю только о выполнении простых однострочных команд оболочки здесь. Когда нам нужно больше контроля над выполнением задачи, я понимаю, что использование модуля subprocess имеет больше смысла, например.

155
Koderok
  1. Это быстрее, os.system и subprocess.call создает новые процессы, которые не нужны для чего-то такого простого. Фактически, os.system и subprocess.call с аргументом Shell обычно создают как минимум два новых процесса: первый - это командная консоль, а второй - выполняемая команда (если это не встроенная оболочка, как test).

  2. Некоторые команды бесполезны в отдельном процессе. Например, если вы запустите os.spawn("cd dir/"), он изменит текущий рабочий каталог дочернего процесса, но не процесса Python. Вы должны использовать os.chdir для этого.

  3. Вам не нужно беспокоиться о специальных интерпретируемых символах оболочкой. os.chmod(path, mode) будет работать независимо от имени файла, тогда как os.spawn("chmod 777 " + path) ужасно потерпит неудачу, если имя файла будет примерно таким же, как ; rm -rf ~. (Обратите внимание, что вы можете обойти это, если используете subprocess.call без аргумента Shell.)

  4. Вам не нужно беспокоиться о имена файлов начинаются с тире. os.chmod("--quiet", mode) изменит права доступа к файлу с именем --quiet, но os.spawn("chmod 777 --quiet") завершится ошибкой, так как --quiet интерпретируется как аргумент. Это верно даже для subprocess.call(["chmod", "777", "--quiet"]).

  5. У вас меньше кросс-платформенных и кросс-Shell проблем, поскольку стандартная библиотека Python должна решить эту проблему для вас. Есть ли в вашей системе команда chmod? Это установлено? Поддерживает ли он параметры, которые вы ожидаете поддерживать? Модуль os постарается быть максимально кроссплатформенным и документировать, когда это невозможно.

  6. Если команда, которую вы запускаете, имеет output, о которой вы заботитесь, вам нужно проанализировать ее, что сложнее, чем кажется, поскольку вы можете забыть о угловых падежах (имена файлов с пробелами, табуляцией и символами новой строки) в них), даже если вы не заботитесь о переносимости.

325
Flimm

Это безопаснее. Чтобы дать вам идею вот пример сценария

import os
file = raw_input("Please enter a file: ")
os.system("chmod 777 " + file)

Если ввод от пользователя был test; rm -rf ~, это затем удалит домашний каталог.

Вот почему безопаснее использовать встроенную функцию.

Следовательно, почему вы должны использовать подпроцесс вместо системы тоже.

133
iProgram

Существует четыре веских аргумента в пользу предпочтения более специфичных методов Python в модуле os при использовании os.system или модуля subprocess при выполнении команды:

  • Избыточность - порождение другого процесса является избыточным и тратит время и ресурсы.
  • Переносимость - Многие из методов в модуле os доступны на нескольких платформах, в то время как многие команды Shell зависят от ОС.
  • Понимание результатов - порождение процесса для выполнения произвольных команд вынуждает вас анализировать результаты из вывода и понимать if и зачем команда выполнила что-то не так.
  • Безопасность - Процесс может потенциально выполнить любую команду, которую ему дают. Это слабый дизайн, и его можно избежать, используя специальные методы в модуле os.

Резервирование (см. избыточный код ):

На самом деле вы выполняете избыточный "посредник" на пути к возможным системным вызовам (chmod в вашем примере). Этот средний человек - это новый процесс или субшелл.

От os.system :

Выполнить команду (строку) в подоболочке ...

И subprocess это просто модуль для порождения новых процессов.

Вы можете делать то, что вам нужно, не вызывая эти процессы.

Переносимость (см. переносимость исходного кода ):

Цель модуля os заключается в предоставлении общих служб операционной системы, и его описание начинается с:

Этот модуль предоставляет портативный способ использования функциональных возможностей, зависящих от операционной системы.

Вы можете использовать os.listdir как в Windows, так и в Unix. Попытка использовать os.system/subprocess для этой функции заставит вас поддерживать два вызова (для ls/dir) и проверять, в какой операционной системе вы находитесь. Это не так переносимо и будет в дальнейшем вызовет еще большее разочарование (см. Обработка вывода).

Понимание результатов команды:

Предположим, вы хотите перечислить файлы в каталоге.

Если вы используете os.system("ls")/subprocess.call(['ls']), вы можете получить только выходные данные процесса, которые в основном представляют собой большую строку с именами файлов.

Как вы можете отличить файл с пробелом в имени от двух файлов?

Что если у вас нет разрешения перечислять файлы?

Как вы должны сопоставить данные с объектами python?

Это только у меня в голове, и хотя есть решения этих проблем - зачем снова решать проблему, которая была решена для вас?

Это пример следования принципу не повторять себя (часто упоминается как "СУХОЙ") путем not ​​повторения реализации, которая уже существует и доступна для вас бесплатно ,.

Безопасность:

os.system и subprocess являются мощными. Это хорошо, когда вам нужна эта сила, но это опасно, когда вы не нуждаетесь. Когда вы используете os.listdir, вы знать он не может делать ничего другого, кроме списка файлов или возникновения ошибки. Когда вы используете os.system или subprocess для достижения того же поведения, вы можете в конечном итоге сделать то, что вы не хотели делать.

Безопасность впрыска (см. Примеры инъекций в оболочку ):

Если вы используете ввод от пользователя в качестве новой команды, вы в основном дали ему Shell. Это очень похоже на SQL-инъекцию, обеспечивающую оболочку в БД для пользователя.

Примером может служить команда вида:

# ... read some user input
os.system(user_input + " some continutation")

Это можно легко использовать для запуска any произвольного кода с помощью ввода: NASTY COMMAND;# для создания возможного:

os.system("NASTY COMMAND; # some continuation")

Есть много таких команд, которые могут поставить вашу систему под угрозу.

60
Reut Sharabani

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

Кроме того, создание Sub-Shell занимает много времени, поэтому использование команд ОС напрямую повлияет на вашу производительность.

РЕДАКТИРОВАТЬ

У меня было несколько временных тестов:

In [379]: %timeit os.chmod('Documents/recipes.txt', 0755)
10000 loops, best of 3: 215 us per loop

In [380]: %timeit os.system('chmod 0755 Documents/recipes.txt')
100 loops, best of 3: 2.47 ms per loop

In [382]: %timeit call(['chmod', '0755', 'Documents/recipes.txt'])
100 loops, best of 3: 2.93 ms per loop

Внутренняя функция работает более чем в 10 раз быстрее

EDIT2

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

23
volcano

Вызов оболочки зависит от ОС, тогда как в большинстве случаев функции модуля Python os не являются. И это позволяет избежать порождения подпроцесса.

16
JoshRomRock

Это гораздо эффективнее. "Shell" - это просто еще один бинарный файл ОС, который содержит много системных вызовов. Зачем тратить время на создание всего процесса Shell только для одного системного вызова?

Ситуация еще хуже, когда вы используете os.system для чего-то, что не является встроенным в Shell. Вы запускаете процесс Shell, который, в свою очередь, запускает исполняемый файл, который затем (на расстоянии двух процессов) выполняет системный вызов. По крайней мере, subprocess устранило бы необходимость в посредническом процессе Shell.

Это не относится к Python, это. systemd - это такое улучшение времени запуска Linux по той же причине: оно выполняет необходимые системные вызовы, а не порождает тысячу оболочек.

11
MSalters