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


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

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

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

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

6 155

6 ответов:

  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 без ? Он установлен? Поддерживает ли он параметры, которые вы ожидаете от него поддержки? Элемент os модуль будет стараться быть как можно более кросс-платформенным и документы, когда это невозможно.

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

это безопаснее. Чтобы дать вам представление вот пример скрипта

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

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

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

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

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

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

избыточность (см. избыточный код):

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

С os.system:

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

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

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

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

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

этот модуль обеспечивает портативный способ использования операционной системы зависит функциональность.

можно использовать os.listdir как в windows, так и в 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")

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

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

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

EDIT

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

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

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

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

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

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