Фактичне значення 'shell = True' у підпроцесі


260

Я викликаю різні процеси за допомогою subprocessмодуля. Однак у мене є питання.

У таких кодах:

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

і

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

Обидва працюють. Прочитавши документи, я дізнався, що shell=Trueозначає виконання коду через оболонку. Тож це означає, що за відсутності, процес безпосередньо запускається.

Тож, що мені слід віддати перевагу в моєму випадку - мені потрібно запустити процес і отримати його вихід. Яка користь у мене від виклику його зсередини оболонки або поза нею.


21
перша команда неправильна: -lпередається в /bin/sh(оболонку) замість lsпрограми на Unix, якщоshell=True . Аргумент рядка слід використовувати shell=Trueв більшості випадків замість списку.
jfs

1
re "процес безпосередньо запускається": Wut?
allyourcode

9
Твердження «Обидва працюють». про ці 2 дзвінки є невірним та оманливим. Виклики працюють по-різному. Просто перехід від shell=Trueдо Falseі навпаки є помилкою. З документів : "On POSIX with shell = True, (...) Якщо аргументи - це послідовність, перший елемент вказує командний рядок, а будь-які додаткові елементи будуть розглядатися як додаткові аргументи до самої оболонки." У Windows є автоматичне перетворення , яке може бути небажаним.
mbdevpl

Дивіться також stackoverflow.com/q/59641747/874188
tripleee

Відповіді:


183

Перевага від того, щоб не дзвонити через оболонку, полягає в тому, що ви не викликаєте "таємничу програму". У POSIX змінна середовища SHELLкерує тим, який двійковий файл викликається як "оболонка". У Windows немає жодного нащадкового елемента оболонки, лише cmd.exe.

Таким чином, виклик оболонки викликає програму, яку вибирає користувач, і залежить від платформи. Взагалі кажучи, уникайте викликів через оболонку.

Викликання через оболонку дозволяє розширювати змінні середовища та файли глобусів відповідно до звичного механізму оболонки. У системах POSIX оболонка розширює файлові кулі до списку файлів. В операційній системі Windows, файл - Глоб (наприклад, «*. *») Чи не розширюється за рахунок оболонки, так чи інакше (але змінні оточення в командному рядку будуть розширені cmd.exe).

Якщо ви думаєте, що вам потрібно розширення змінних середовищ та файлових кульок, вивчіть ILSнапади 1992-ish на мережеві сервіси, які виконували виклики підпрограми через оболонку. Приклади включають в себе різні sendmailкуточки на задньому плані ILS.

Підводячи підсумок, використовуйте shell=False.


2
Дякую за відповідь. Хоча я насправді не на тому етапі, де мені слід переживати про подвиги, але я розумію, до чого ти потрапляєш.
user225312

55
Якщо ви на початку недбалі, ніякі турботи не допоможуть вам пізніше наздогнати. ;)
Хіт Ханнікутт

Що робити, якщо ви хочете обмежити максимальну пам'ять підпроцесу? stackoverflow.com/questions/3172470/…
Pramod

8
твердження про $SHELLне є правильним. Процитуйте subprocess.html: "У Unix з shell=True, оболонка за замовчуванням /bin/sh." (не $SHELL)
marcin

1
@ user2428107: Так, якщо ви використовуєте виклик зворотного вибору на Perl, ви використовуєте виклик оболонки та відкриваєте ті самі проблеми. Використовуйте аргумент 3+, openякщо ви хочете захистити способи викликати програму та захопити вихід.
ShadowRanger

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

Якщо встановити аргумент оболонки на справжнє значення, змушує підпроцес породити проміжний процес оболонки і наказати йому запустити команду. Іншими словами, використання проміжної оболонки означає, що змінні, шаблони глоба та інші спеціальні функції оболонки в командному рядку обробляються перед запуском команди. Тут, у прикладі, перед командою echo було оброблено $ HOME. Власне, це випадок команди з розширенням оболонки, тоді як команда ls -l розглядається як проста команда.

джерело: Підпроцесорний модуль


16
Не знаю, чому це не обрана відповідь. Напевно той, що насправді відповідає питанню
Родріго Лопес Ґерра,

1
згоден. це хороший приклад для мене, щоб зрозуміти, що означає оболонка = True.
користувач389955

2
Якщо встановити аргумент оболонки на справжнє значення, змушує підпроцес породити проміжний процес оболонки і наказати йому виконати команду О боже, це все говорить. Чому ця відповідь не приймається ??? чому?
pouya

Я думаю, що питання є першим аргументом для виклику - це список, а не рядок, але це дає помилку, якщо оболонка є помилковою. Зміна команди на список зробить цю роботу
Лінкольн Рендалл Макфарланд

Вибачте, що мій попередній коментар пішов ще до того, як я закінчив. Щоб було зрозуміло: я часто бачу використання підпроцесу з shell = True і команда - це рядок, наприклад, 'ls -l', (я очікую, щоб уникнути цієї помилки), але підпроцес приймає список (і рядок як один список елементів) . Для запуску з викликом оболонки (і проблем із безпекою із цим ) використовуйте список subprocess.call (['ls', '-l'])
Лінкольн Рендалл Макфарланд

42

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


6
Посилання дуже корисне. Як зазначається в посиланні: Виконання команд оболонки, що містять несанізований вхід з ненадійного джерела, робить програму вразливою до введення оболонки, серйозний недолік безпеки, який може призвести до довільного виконання команд. З цієї причини використання shell = True сильно не рекомендується у випадках, коли командний рядок побудований із зовнішнього вводу.
jtuki

39

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

shell=Trueіноді зручно використовувати конкретні функції оболонки, такі як розділення слів або розширення параметрів. Однак якщо така функція потрібна, скористайтеся іншими модулями, наданими вам (наприклад, os.path.expandvars()для розширення параметрів або shlexдля розбиття слів). Це означає більше роботи, але уникає інших проблем.

Коротше кажучи: уникайте shell=Trueбудь-якими способами.


16

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

Там, де взаємодія з оболонкою нетривіальна, тепер ви вимагаєте від читача та підтримувача сценарію Python (який може бути, а може і не бути вашим майбутнім "), щоб зрозуміти і Python, і сценарій оболонки. Запам’ятайте девіз Python «явне краще, ніж неявне»; навіть коли код Python буде дещо складнішим, ніж еквівалентний (і часто дуже короткий) скрипт оболонки, вам може бути краще видалити оболонку та замінити функціональність на рідними конструкціями Python. Мінімізація роботи, виконаної у зовнішньому процесі, і максимально тримати контроль над власним кодом, часто є хорошою ідеєю просто тому, що це покращує видимість і зменшує ризики виникнення бажаних або небажаних побічних ефектів.

Розширення підстановки, змінна інтерполяція та перенаправлення все просто замінити рідними конструкціями 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немає виводу для збору.

Закінчу цитатою Девіда Корна: "Простіше написати портативний оболонку, ніж сценарій портативної оболонки". Навіть subprocess.run('echo "$HOME"', shell=True)не є портативним для Windows.


Я думав, що цитата від Ларрі Уолла, але Google говорить мені інакше.
трійчатка

Це велика розмова - але жодна технічна пропозиція щодо заміни: Ось я, на OS-X, намагаюся придбати pid Mac App, який я запустив через 'open': process = subprocess.Popen ('/ usr / bin / pgrep - n '+ app_name, shell = False, stdout = subprocess.PIPE, stderr = subprocess.PIPE) app_pid, err = process.communicate () --- але це не працює, якщо я не буду використовувати shell = True. А тепер що?
Motti Shneor

Є багато питань про те, як їх уникнути shell=True, багато - з відмінними відповідями. Вам трапилось вибрати саме те, про що, замість цього.
тріплей

@MottiShneor Дякую за відгук; додав простий приклад
tripleee

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