Навіщо використовувати методи модуля ос на Python замість виконання команд оболонки безпосередньо?


157

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

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

Я розумію, що "пітонічніше" максимально використовувати доступні методи бібліотеки Python, а не просто виконувати команди оболонок безпосередньо. Але чи є якась інша мотивація, що робить це з точки зору функціональності?

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


6
Ви в основному вдарили нігтем по голові. Завдання на рівні ОС, на які ви посилаєтеся, досить поширені, що вони гарантували власну функцію, а не просто перекидалися на виклик через os.system.
deweyredman

7
BTW, ви намагалися вчасно виконати час виконання - os.chmod vs. os.system ("chmod ...") . Я б ризикнув здогадатися, що це відповість на частину вашого запитання.
вулкан

61
Чому так, printколи ти міг os.system("echo Hello world!")?
користувач253751

25
З тієї ж причини ви повинні використовувати os.pathдля обробки шляхів замість того, щоб обробляти їх вручну: вона працює в будь-якій ОС, де вона працює.
Бакуріу

51
"Виконання команд оболонки безпосередньо" насправді менш пряме. Оболонка не є низькорівневим інтерфейсом до системи, і os.chmodне збирається викликати chmodпрограму, яку би оболонка. Використання os.system('chmod ...')запускає оболонку для інтерпретації рядка для виклику іншого виконуваного файлу , щоб зробити виклик на C chmodфункцію, в той час як os.chmod(...)йде набагато більш безпосередньо до C chmod.
user2357112 підтримує Моніку

Відповіді:


325
  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. У вас менше проблем з платформою та перехресними оболонками, оскільки стандартна бібліотека Python повинна вирішити це для вас. Чи має ваша система chmodкомандування? Він встановлений? Чи підтримує він параметри, які ви очікуєте, що він підтримує? osМодуль буде намагатися бути крос-платформних , як це можливо і документів , коли , що це не можливо.

  6. Якщо команда, яку ви виконуєте, має вихід, який вас цікавить, вам потрібно її проаналізувати, що складніше, ніж це звучить, оскільки ви можете забути про кутові регістри (назви файлів з пробілами, вкладками та новими рядками), навіть коли ви не переймайтеся портативністю.


38
Щоб додати до точки "крос-платформи", перерахування каталогу "ls" на Linux, "dir" у Windows. Отримання вмісту каталогу - дуже поширене завдання низького рівня.
Корт Аммон

1
@CortAmmon: «Low-Level» є відносним, lsабо dirдосить високий рівень для певних типів розробників, так само , як bashі cmdчи kshабо будь-який інший оболонки ви віддаєте перевагу є.
Себастьян Мах

1
@phresnel: Я ніколи про це не думав. Для мене "прямий дзвінок до API ядра вашої ОС" був дуже низьким. Я припускаю, що щодо цього є інша точка зору, яка мені ухиляється, оскільки я (природно) підходжу до цього з власними упередженнями.
Корт Аммон

5
@CortAmmon: правильно і lsвище, ніж це, оскільки це не прямий дзвінок до API ядра вашої ОС. Це (невелике) додаток.
Стів Джессоп

1
@SteveJessop. Я назвав низький рівень "отримання вмісту каталогу". Я не думаю , lsабо , dirале opendir()/readdir()(Linux API) або FindFirstFile()/FindNextFile()(Windows API) або File.listFiles(Java API) або Directory.GetFiles()(C #). Все це тісно пов'язане з прямим дзвінком в ОС. Деякі можуть бути такими ж простими, як введення номера в реєстр і виклик int 13hдля запуску режиму ядра.
Корт Аммон

133

Це безпечніше. Щоб дати вам уявлення, ось приклад сценарію

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

Якщо вхід від користувача був test; rm -rf ~таким, то видалить домашній каталог.

Ось чому безпечніше використовувати вбудовану функцію.

Отже, вам слід також використовувати підпроцес замість системи.


26
Або інший спосіб поглянути на це, що легше отримати правильно, написання програм Python або написання програм Python, які пишуть сценарії оболонки? :-)
Стів Джессоп

3
@SteveJessop, мій колега був вражений тим, що маленький сценарій Python, який я допоміг йому написати, працював у 20 (!) Разів швидший сценарій оболонки tan. Я пояснив, що перенаправлення виходу може виглядати сексуально - але це тягне за собою відкриття та закриття файлу на кожній ітерації. Але деякі люблять робити складний склад - :)
вулкан

1
@SteveJessop, це хитрі питання - ви не знали б до виконання! :)

60

Існує чотири важкі випадки, коли віддавати перевагу більш конкретним методам Python в osмодулі над використанням os.systemабо subprocessмодулем під час виконання команди:

  • Надлишок - нерест іншого процесу є зайвим і витрачає час та ресурси.
  • Переносимість - Багато методів у osмодулі доступні на декількох платформах, в той час як багато команд оболонки мають специфічну для ОС.
  • Розуміння результатів - Нерестування процесу виконання довільних команд змушує вас розібрати результати з результату та зрозуміти, якщо і чому команда зробила щось не так.
  • Безпека - процес може потенційно виконувати будь-яку задану йому команду. Це слабка конструкція, і цього можна уникнути, використовуючи конкретні методи в osмодулі.

Надлишок (див. Надлишковий код ):

Ви насправді виконуєте зайвого «середнього чоловіка» на шляху до можливих системних викликів ( chmodу вашому прикладі). Цей середній чоловік - це новий процес або підрозділ.

Від os.system:

Виконайте команду (рядок) у нижній частині ...

І subprocessце лише модуль для нересту нових процесів.

Ви можете робити все, що вам потрібно, не породжуючи цих процесів.

Переносність (див. Портативність вихідного коду ):

Метою osмодуля є надання загальних послуг операційної системи, і його опис починається з:

Цей модуль пропонує портативний спосіб використання функціональних можливостей, залежних від операційної системи.

Ви можете використовувати os.listdirяк Windows, так і Unix. Спроба використовувати os.system/ subprocessдля цієї функції змусить вас підтримувати два дзвінки (для ls/ dir) і перевірити, на якій операційній системі ви працюєте. Це не так портативно, і згодом викличе ще більше розчарувань (див. Поводження з результатами ).

Розуміння результатів команди:

Припустимо, ви хочете перерахувати файли в каталозі.

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

Як ви можете розпізнати файл із пробілом у його імені з двох файлів?

Що робити, якщо у вас немає дозволу на перелік файлів?

Як слід відображати дані на об’єкти python?

Це лише у верхній частині моєї голови, і поки є рішення цих проблем - навіщо знову вирішувати проблему, вирішену для вас?

Це приклад дотримання принципу " Не повторюй себе" (часто його називають "DRY"), не повторюючи реалізацію, яка вже існує і доступна для вас.

Безпека:

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

Є багато таких команд, які можуть піддавати вашій системі небезпеку.


3
Я б сказав 2. є основною причиною.
jaredad7

23

З простої причини - коли ви викликаєте функцію оболонки, вона створює під-оболонку, яка знищується після існування вашої команди, тому, якщо ви змінюєте каталог в оболонці - це не впливає на ваше оточення в 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, який він використовував. Але, звичайно, не тоді, коли ми говоримо про стандартні пакети ОС, що імітують стандартні команди ОС


Чи випадково це робиться з iPython? Не думав, що ти можеш використовувати спеціальні функції, починаючи з %звичайного інтерпретатора.
iProgram

@aPyDeveloper, так, це був iPython - на Ubuntu. "Чарівний" % timeit - це благо, хоча бувають випадки, в основному зі струнним форматуванням, які він не може обробити
вулкан

1
Або ви також можете створити скрипт python, а потім набрати time <path to script> термінал, і він розповість вам про реальний, користувальницький і час роботи. Тобто якщо у вас немає iPython і у вас є доступ до командного рядка Unix.
iProgram

1
@aPyDeveloper, я не бачу причин для того, щоб наполегливо працювати - коли у мене є iPython на моїй машині
вулкан

Правда! Я сказав, якщо у вас не було iPython. :)
iProgram

16

Виклик оболонки є специфічним для ОС, тоді як функції модуля Python os в більшості випадків відсутні. І це уникає нересту підпроцесу.


1
Функції модуля Python також породжують нові підпроцеси, щоб викликати нову підпакет.
Кодерок

7
@Koderok нісенітниця, модульні функції викликаються в процесі
dwurf

3
@Koderok: os-модуль використовує основні системні виклики, які використовувала команда shell, він не використовує команди оболонки. Це означає, що системний виклик OS зазвичай безпечніший та швидший (без розбору рядків, boo fork, no exec, натомість це лише виклик ядра), ніж команди оболонки. Зауважте, що у більшості випадків виклик оболонки та системний виклик часто мають аналогічні або однакові назви, але документуються окремо; виклик оболонки знаходиться у розділі man (1 людина (за замовчуванням), тоді як системний виклик, еквівалентно названий, знаходиться в розділі man (наприклад, man 2 chmod).
Лежати Райан

1
@ dwurf, LieRyan: Моє погано! У мене було неправильне поняття, схоже. Дякую!
Koderok

11

Це набагато ефективніше. "Оболонка" - це ще один двійковий файл ОС, який містить безліч системних викликів. Навіщо брати на себе витрати на створення всього процесу оболонки саме для цього єдиного системного виклику?

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

Це не конкретно для Python. systemdтаке поліпшення часу запуску Linux з тієї ж причини: вона робить необхідні системні виклики себе замість нерестування тисячі оболонок.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.