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

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

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

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

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

Ответ 1

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

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

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

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

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

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

Ответ 2

Это безопаснее. Чтобы дать вам представление, вот пример script

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

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

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

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

Ответ 3

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

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

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

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

Из os.system:

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

И subprocess является всего лишь модулем для создания новых процессов.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 4

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

Кроме того, создание суб-оболочки требует много времени, поэтому использование команд OS напрямую повлияет на вашу производительность

ИЗМЕНИТЬ

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

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, который он использовал. Но, конечно, не тогда, когда мы говорим о стандартных пакетах ОС, имитирующих стандартные команды OS

Ответ 5

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

Ответ 6

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

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

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