Виклик зовнішньої команди з Python


4881

Як ви викликаєте зовнішню команду (як би я набрав її в оболонці Unix або командному рядку Windows) із сценарію Python?

Відповіді:


4692

Подивіться на підпроцесорний модуль у стандартній бібліотеці:

import subprocess
subprocess.run(["ls", "-l"])

Перевага subprocessпорівняно з systemтим, що він є більш гнучким (ви можете отримати stdout, " stderr," справжній код статусу, краще обробляти помилки тощо ...).

Офіційна документація рекомендує subprocessмодуль над альтернативою os.system():

subprocessМодуль надає більш потужні засоби для породження нових процесів і отримання їх результатів; використання цього модуля краще використовувати цю функцію [ os.system()].

У заміні старих функцій з допомогою подпроцесса модуля розділ в subprocessдокументації може мати деякі корисні рецепти.

Для версій Python до 3.5 використовуйте call:

import subprocess
subprocess.call(["ls", "-l"])

Чи існує спосіб використання змінної підстановки? IE, що я намагався зробити echo $PATH, використовуючи call(["echo", "$PATH"]), але це просто перегукується з буквальним рядком $PATHзамість того, щоб робити будь-яку заміну. Я знаю, що міг би отримати змінну середовища PATH, але мені цікаво, чи існує простий спосіб змусити команду вести себе точно так, як ніби я її виконав у bash.
Кевін Вілер

@KevinWheeler Для роботи вам доведеться використовувати shell=Trueце.
SethMMorton

38
@KevinWheeler НЕ слід використовувати shell=True, для цього Python постачається з os.path.expandvars . У вашому випадку ви можете написати: os.path.expandvars("$PATH"). @SethMMorton, будь ласка, перегляньте свій коментар -> Чому б не скористатися shell = True
Murmel

блокує дзвінок? тобто якщо я хочу запустити кілька команд у forциклі, як це зробити, не блокуючи мій скрипт python? Мене не хвилює вихід команди, я просто хочу запустити їх багато.
Чарлі Паркер

4
Якщо ви хочете створити список із команди з параметрами , списку, з яким можна використовувати subprocessколи shell=False, тоді скористайтеся shlex.splitпростим способом зробити це docs.python.org/2/library/shlex.html#shlex.split
Даніель Ф

2982

Ось підсумок способів виклику зовнішніх програм та переваг та недоліків кожної з них:

  1. os.system("some_command with args")передає команду та аргументи в оболонку вашої системи. Це добре, тому що ви можете реально запускати декілька команд одночасно таким чином і налаштовувати труби та перенаправлення вводу / виводу. Наприклад:

    os.system("some_command < input_file | another_command > output_file")  

Однак, хоча це зручно, вам доведеться вручну обробляти вхідні символи оболонки, такі як пробіли тощо. З іншого боку, це також дозволяє запускати команди, які є просто командними оболонками, а насправді не зовнішніми програмами. Дивіться документацію .

  1. stream = os.popen("some_command with args")зробить те саме, що, os.systemза винятком того, що він дає вам файл-подібний об'єкт, який ви можете використовувати для доступу до стандартного вводу / виводу для цього процесу. Існують 3 інші варіанти popen, які всі обробляють введення / виведення трохи по-різному. Якщо ви передаєте все як рядок, то ваша команда передається оболонці; якщо ви передаєте їх як список, то вам не потрібно турбуватися про те, щоб уникнути нічого. Дивіться документацію .

  2. PopenКлас subprocessмодуля. Це призначене як заміна, os.popenале має і менший бік бути дещо складнішим через те, що він є настільки вичерпним. Наприклад, ви скажете:

    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()

    замість:

    print os.popen("echo Hello World").read()

    але приємно мати всі параметри в одному об'єднаному класі замість 4-х різних функцій. Дивіться документацію .

  3. callФункція від subprocessмодуля. Це в основному так само, як і Popenклас, і приймає всі ті ж аргументи, але він просто чекає, поки команда завершиться і надасть вам код повернення. Наприклад:

    return_code = subprocess.call("echo Hello World", shell=True)  

    Дивіться документацію .

  4. Якщо ви перебуваєте на Python 3.5 або пізнішої версії, ви можете використовувати нову subprocess.runфункцію, яка дуже схожа на вищезазначені, але ще більш гнучку і повертає CompletedProcessоб’єкт, коли команда закінчить виконання.

  5. Модуль os також має всі функції fork / exec / spawn, які ви мали б у програмі C, але я не рекомендую використовувати їх безпосередньо.

Можливо, subprocessмодуль повинен бути тим, що ви використовуєте.

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

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

і уявіть, що користувач вводить щось "моя мама не любила мене && rm -rf /", що могло б стерти всю файлову систему.


22
Приємна відповідь / пояснення. Як ця відповідь виправдовує девіз Python, як описано в цій статті? fastcompany.com/3026446/… "Стилістично, Perl і Python мають різні філософії. Найвідоміші девізи Perl -" Існує більше, ніж один спосіб зробити це ". інший шлях! У Perl я знаю лише два способи виконання команди - за допомогою зворотного галочки або open.
Жан

12
Якщо ви використовуєте Python 3.5+, використовуйте subprocess.run(). docs.python.org/3.5/library/subprocess.html#subprocess.run
Phoenix

4
Що зазвичай потрібно знати, це те, що робиться з дочірнім процесом STDOUT та STDERR, тому що якщо вони ігноруються, за певних (досить поширених) умов, врешті-решт дочірній процес видасть системний виклик для запису в STDOUT (також STDERR?) що перевищить вихідний буфер, передбачений процесом в ОС, і ОС призведе до блокування, поки деякий процес не зчитує з цього буфера. Отже, із рекомендованими на даний момент способами, subprocess.run(..)що саме означає "Це не зафіксує stdout або stderr за замовчуванням." мати на увазі? А як щодо subprocess.check_output(..)STDERR?
Євгеній Сергєєв

2
@Pitto так, але це не те, що виконується на прикладі. Помітьте, що echoпередні рядки передані Popen? Тож повна команда буде echo my mama didnt love me && rm -rf /.
Кріс Арндт

6
Це, мабуть, неправильний шлях. Більшість людей потребує лише subprocess.run()своїх старших братів subprocess.check_call()та сестер та ін. Про випадки, коли їх недостатньо, див subprocess.Popen(). os.popen()можливо, не варто згадувати взагалі, або прийти навіть після "зламання власного коду fork / exec / spawn".
трійчатка

357

Типова реалізація:

import subprocess

p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
    print line,
retval = p.wait()

Ви вільні робити те, що ви хочете, з stdoutданими, що знаходяться в трубці. Насправді ви можете просто опустити ці параметри ( stdout=і stderr=), і вони будуть вести себе так os.system().


44
.readlines()зчитує відразу всі рядки, тобто блокує, поки підпроцес не вийде (закриє його кінець труби). Щоб читати в режимі реального часу (якщо проблем із буферизацією немає), ви можете:for line in iter(p.stdout.readline, ''): print line,
jfs

1
Не могли б ви детальніше розібратися з тим, що ви маєте на увазі під "якщо проблем із буферизацією немає"? Якщо процес безумовно блокує, дзвінок підпроцесу також блокується. Те саме могло статися і з моїм оригінальним прикладом. Що ще може статися щодо буферизації?
EmmEff

15
дочірній процес може використовувати блок-буферизацію в неінтерактивному режимі замість буферизації ліній, тому p.stdout.readline()(зверніть увагу: ні sв кінці) не буде бачити даних, поки дитина не заповнить свій буфер. Якщо дитина не видає багато даних, то результат не буде в режимі реального часу. Дивіться другу причину в питанні: Чому б не просто використовувати трубу (popen ())? . У цій відповіді містяться деякі обхідні варіанти (pexpect, pty, stdbuf)
jfs

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

3
Ця відповідь була чудовою для свого часу, але ми не повинні більше рекомендувати Popenдля простих завдань. Це також непотрібно вказувати shell=True. Спробуйте одну з subprocess.run()відповідей.
трійка

230

Деякі натяки на відмежування процесу дитини від виклику (запуск дочірнього процесу у фоновому режимі).

Припустимо, ви хочете почати довге завдання зі сценарію CGI. Тобто, дочірній процес повинен жити довше, ніж процес виконання сценарію CGI.

Класичний приклад документації підпроцесорного модуля:

import subprocess
import sys

# Some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # Call subprocess

# Some more code here

Ідея тут полягає в тому, що ви не хочете чекати в рядку "виклик підпроцесу" до завершення longtask.py. Але незрозуміло, що станеться після рядка "ще якийсь код тут" із прикладу.

Моєю цільовою платформою був FreeBSD, але розробка була в Windows, тому я спочатку зіткнувся з проблемою в Windows.

У Windows (Windows XP) батьківський процес не закінчиться, поки longtask.py не закінчить свою роботу. Це не те, що потрібно в сценарії CGI. Проблема не є специфічною для Python; у спільноті PHP проблеми однакові.

Рішення полягає в тому, щоб передати DETACHED_PROCESS прапор створення процесу основній функції CreateProcess в API Windows. Якщо у вас інстальований pywin32, ви можете імпортувати прапор з модуля win32process, інакше слід визначити його самостійно:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

/ * UPD 2015.10.27 @eryksun у коментарі нижче зазначає, що семантично правильний прапор - CREATE_NEW_CONSOLE (0x00000010) * /

У FreeBSD у нас є ще одна проблема: коли батьківський процес закінчений, він також закінчує дочірні процеси. І це не те, що потрібно в сценарії CGI. Деякі експерименти показали, що проблема, здавалося, полягає у спільному використанні sys.stdout. І робочим рішенням було таке:

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

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


Я помітив можливу "химерність" з розробкою програм py2exe в pydev + затемнення. я зміг сказати, що основний сценарій не від'єднаний, оскільки вихідне вікно затемнення не закінчувалося; навіть якщо сценарій виконується до завершення, він все ще чекає повернення. але, коли я намагався компілювати до виконуваного файлу py2exe, виникає очікувана поведінка (запускає процеси як від'єднані, то закриває). Я не впевнений, але ім'я виконавця більше не в списку процесів. це працює для всіх підходів (os.system ("start *"), os.spawnl з os.P_DETACH, підпрограми тощо)
maranas

1
вам також може знадобитися CREATE_NEW_PROCESS_GROUP прапор. Дивіться, як Попен чекає дитячого процесу навіть тоді, коли закінчиться безпосередня дитина
jfs

5
Неправильне наступне: "[o] n windows (win xp), батьківський процес не закінчиться, поки longtask.py не закінчить свою роботу". Батьківський вихід вийде нормально, але вікно консолі (екземпляр conhost.exe) закривається лише тоді, коли завершиться останній доданий процес, і дитина, можливо, успадкувала батьківську консоль. Установка DETACHED_PROCESSв creationflagsуникаєш цього шляху запобігання дитини від успадкування або створення консолі. Якщо ви хочете отримати нову консоль, використовуйте CREATE_NEW_CONSOLE(0x00000010).
Ерік Нд

1
Я не мав на увазі, що виконання як від'єднаного процесу є невірним. Однак, можливо, вам знадобиться встановити стандартні ручки для файлів, труб або os.devnullтому, що деякі консольні програми виходять із помилкою в іншому випадку. Створіть нову консоль, коли ви хочете, щоб дочірній процес взаємодів із користувачем одночасно з батьківським процесом. Було б заплутано намагатися робити обидва в одному вікні.
Ерик Нд

1
чи не існує оперативно-оперативного способу запустити процес у фоновому режимі?
Чарлі Паркер

151
import os
os.system("your command")

Зауважте, що це небезпечно, оскільки команда не очищена. Я залишаю вам google для отримання відповідної документації на модулі 'os' та 'sys'. Є купа функцій (exec * та spawn *), які дозволять робити подібні дії.


6
Поняття не маю на увазі, що я мав на увазі майже десять років тому (перевірити дату!), Але якби я мав здогадуватися, було б, що перевірки не зроблено.
nimish

1
Тепер це повинно вказувати subprocessяк на дещо більш універсальне та портативне рішення. Запуск зовнішніх команд, звичайно, неприйнятний (ви повинні переконатися, що команда доступна в будь-якій архітектурі, яку вам потрібно підтримувати), а передача даних користувача як зовнішньої команди по своїй суті небезпечна.
трійка

1
Зверніть увагу на часову позначку цього хлопця: відповідь "правильна" має 40 разів голосів і є відповідь №1.
nimish

Єдине рішення, яке працювало для мене для роботи з NodeJS.
Микола Шиндаров

147

Я рекомендую використовувати підпроцесорний модуль замість os.system, тому що він робить для вас оболонку і тому набагато безпечніший.

subprocess.call(['ping', 'localhost'])

Якщо ви хочете створити список із команди з параметрами , списку, з яким можна використовувати, subprocessколи shell=False, тоді скористайтеся shlex.splitпростим способом зробити це docs.python.org/2/library/shlex.html#shlex.split ( рекомендований спосіб відповідно до docs.python.org/2/library/subprocess.html#popen-constructor )
Даніель F,

6
Це невірно: " це втеча з оболонки для вас і тому набагато безпечніше ". підпроцес не виконує втечу оболонки, підпроцес не передає вашу команду через оболонку, тому не потрібно бігати з оболонки.
Лежи Райан

143
import os
cmd = 'ls -al'
os.system(cmd)

Якщо ви хочете повернути результати команди, ви можете використовувати os.popen. Однак це застаріло з версії 2.6 на користь модуля підпроцесу , який інші відповіді добре висвітлювали.



Ви також можете зберегти свій результат за допомогою виклику os.system, оскільки він працює як сама оболонка UNIX, як, наприклад, os.system ('ls -l> test2.txt')
Стефан Груенвальд

97

Існує безліч різних бібліотек, які дозволяють викликати зовнішні команди з Python. Для кожної бібліотеки я описав і показав приклад виклику зовнішньої команди. Команда, яку я використала як приклад, є ls -l(перерахуйте всі файли). Якщо ви хочете дізнатися більше про будь-яку бібліотеку, яку я перераховував, та зв'язали документацію для кожної з них.

Джерела:

Це всі бібліотеки:

Сподіваємось, це допоможе вам прийняти рішення, якою бібліотекою користуватися :)

підпроцес

Підпроцес дозволяє викликати зовнішні команди та підключити їх до їх вхідних / вихідних / помилкових труб (stdin, stdout та stderr). Підпроцес - вибір за замовчуванням для запуску команд, але іноді інші модулі є кращими.

subprocess.run(["ls", "-l"]) # Run command
subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output
subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command

ос

os використовується для "функціональності, залежної від операційної системи". Його також можна використовувати для виклику зовнішніх команд за допомогою os.systemта os.popen(Примітка. Існує також підпроцес.popen). os завжди буде запускати оболонку і є простою альтернативою для людей, яким не потрібно або не знають, як їх використовувати subprocess.run.

os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output

ш

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

sh.ls("-l") # Run command normally
ls_cmd = sh.Command("ls") # Save command as a variable
ls_cmd() # Run command as if it were a function

плюмбум

plumbum - це бібліотека програм "Python", подібних до сценарію. Ви можете викликати такі програми, як функції sh. Плумбум корисний, якщо ви хочете провести трубопровід без оболонки.

ls_cmd = plumbum.local("ls -l") # get command
ls_cmd() # run command

pexpect

pexpect дозволяє нерестувати додатки, керувати ними та знаходити шаблони у їх результатах. Це краща альтернатива підпроцесу для команд, які очікують tty на Unix.

pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo user@example.com:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')

тканина

тканина - бібліотека Python 2.5 та 2.7. Це дозволяє виконувати локальні та віддалені команди оболонки. Тканина - це проста альтернатива запуску команд у захищеній оболонці (SSH)

fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output

посланник

Посланець відомий як "підпроцес для людини". Він використовується як зручна обгортка навколо subprocessмодуля.

r = envoy.run("ls -l") # Run command
r.std_out # get output

команди

commandsмістить функції обгортки для os.popen, але він був видалений з Python 3, оскільки subprocessє кращою альтернативою.

Редагування було засноване на коментарі Дж. Ф. Себастьяна.


74

Я завжди використовую fabricдля цього такі речі, як:

from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )

Але це, здається, є хорошим інструментом: sh(інтерфейс підпроцесу Python) .

Подивіться на приклад:

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)

73

Перевірте також "pexpect" бібліотеку Python.

Це дозволяє інтерактивне управління зовнішніми програмами / командами, навіть ssh, ftp, telnet тощо. Ви можете просто ввести щось на зразок:

child = pexpect.spawn('ftp 192.168.0.24')

child.expect('(?i)name .*: ')

child.sendline('anonymous')

child.expect('(?i)password')

70

Зі стандартною бібліотекою

Використовуйте модуль підпроцесу (Python 3):

import subprocess
subprocess.run(['ls', '-l'])

Це рекомендований стандартний спосіб. Однак складніші завдання (труби, вихід, вхід тощо) можуть бути стомлюючими для побудови та написання.

Примітка щодо версії Python: Якщо ви все ще використовуєте Python 2, subprocess.call працює аналогічно.

ProTip: shlex.split може допомогти вам розібрати команду для run, callта інших subprocessфункцій, якщо ви не хочете (або не можете!) Надавати їх у вигляді списків:

import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))

З зовнішніми залежностями

Якщо ви не проти зовнішніх залежностей, використовуйте плюмбум :

from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())

Це найкраща subprocessобгортка. Це крос-платформа, тобто працює як в Windows, так і в Unix-подібних системах. Встановити pip install plumbum.

Інша популярна бібліотека ш :

from sh import ifconfig
print(ifconfig('wlan0'))

Однак shпідтримка Windows відмовилася, тож це не так приголомшливо, як раніше. Встановити pip install sh.


69

Якщо вам потрібен вихід з команди, яку ви викликаєте, ви можете використовувати subprocess.check_output (Python 2.7+).

>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'

Також зверніть увагу на параметр оболонки .

Якщо оболонка є True, то вказана команда буде виконуватися через оболонку. Це може бути корисно, якщо ви використовуєте Python в першу чергу для покращеного потоку управління, який він пропонує в більшості системних оболонок, і все ще хочете зручного доступу до інших функцій оболонки, таких як труби оболонки, подстановочні імена файлів, розширення змінної середовища та розширення ~ до будинку користувача каталог. Тим НЕ менше, зверніть увагу , що сам Python пропонує реалізацію багатьох оболочечной подібні функції (зокрема, glob, fnmatch, os.walk(), os.path.expandvars(), os.path.expanduser(), і shutil).


1
Зверніть увагу, що check_outputпотрібен список, а не рядок. Якщо ви не покладаєтесь на процитовані пробіли, щоб зробити ваш дзвінок дійсним, найпростіший, найчитабельніший спосіб зробити це subprocess.check_output("ls -l /dev/null".split()).
Бруно Броноський

56

Ось так я виконую свої команди. У цьому коді є все, що вам дуже потрібно

from subprocess import Popen, PIPE
cmd = "ls -l ~/"
p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
print "Return code: ", p.returncode
print out.rstrip(), err.rstrip()

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

54

Оновлення:

subprocess.run- це рекомендований підхід для Python 3.5, якщо ваш код не потребує підтримки сумісності з більш ранніми версіями Python. Він більш послідовний і пропонує подібну простоту використання, як і Envoy. (Хоча трубопровід не такий простий. Щоб дізнатися, як це зробити .).

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

Запустити процес:

>>> subprocess.run(["ls", "-l"])  # Doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)

Підняття на невдалий пробіг:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

Захоплення виводу:

>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')

Оригінальна відповідь:

Я рекомендую спробувати посланника . Це обгортка для підпроцесу, яка в свою чергу спрямована на заміну старих модулів і функцій. Посланець - це підпроцес для людей.

Приклад використання з README :

>>> r = envoy.run('git config', data='data to pipe in', timeout=2)

>>> r.status_code
129
>>> r.std_out
'usage: git config [options]'
>>> r.std_err
''

Труби теж:

>>> r = envoy.run('uptime | pbcopy')

>>> r.command
'pbcopy'
>>> r.status_code
0

>>> r.history
[<Response 'uptime'>]


36

Виклик зовнішньої команди в Python

Простий у використанні subprocess.run, який повертає CompletedProcessоб'єкт:

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

Чому?

Станом на Python 3.5, документація рекомендує subprocess.run :

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

Ось приклад найпростішого можливого використання - і він робить саме так, як було запропоновано:

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

runчекає успішного завершення команди, а потім повертає CompletedProcessоб'єкт. Натомість він може підняти TimeoutExpired(якщо ви дасте йому timeout=аргумент) або CalledProcessError(якщо це не вдасться, і ви перейдете check=True).

Як ви можете зробити з наведеного вище прикладу, і stdout і stderr отримують конфігурацію до власного stdout та stderr за замовчуванням.

Ми можемо перевірити повернений об’єкт і побачити команду, яку було надано, і код повернення:

>>> completed_process.args
'python --version'
>>> completed_process.returncode
0

Захоплення виходу

Якщо ви хочете отримати результат, ви можете перейти subprocess.PIPEдо відповідного stderrабо stdout:

>>> cp = subprocess.run('python --version', 
                        stderr=subprocess.PIPE, 
                        stdout=subprocess.PIPE)
>>> cp.stderr
b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
>>> cp.stdout
b''

(Мені здається цікавим і дещо протилежним, що відомості про версію ставлять у stderr замість stdout.)

Передайте список команд

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

>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = subprocess.run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\r\n  This is indented.\r\n'

Зауважте, що argsслід передавати лише позиційно.

Повний підпис

Ось фактичний підпис у джерелі та як показано help(run):

def run(*popenargs, input=None, timeout=None, check=False, **kwargs):

popenargsІ kwargsнаведені в Popenконструкторі. inputможе бути рядок байтів (або unicode, якщо вказати кодування або universal_newlines=True), які будуть передані в stdin підпроцесу.

Документація описує timeout=і check=Trueкраще, ніж я міг:

Аргумент часу очікування передається Popen.communicate (). Якщо час закінчиться, дочірній процес буде вбито і чекають. Виняток TimeoutExpired буде відновлено після завершення дочірнього процесу.

Якщо перевірка вірна, і процес закінчується з ненульовим кодом виходу, буде винесено виняток CalledProcessError. Атрибути цього винятку містять аргументи, код виходу, а також stdout та stderr, якщо вони були захоплені.

і цей приклад для check=Trueкращого, ніж я міг би придумати:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

Розширений підпис

Ось розширений підпис, як зазначено в документації:

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, 
shell=False, cwd=None, timeout=None, check=False, encoding=None, 
errors=None)

Зауважте, що це вказує на те, що лише список аргументів повинен передаватися позиційно. Тому передайте решта аргументів як аргументи ключових слів.

Попен

Коли Popenзамість цього використовувати ? Я б намагався знайти корисний випадок лише на основі аргументів. PopenОднак, безпосередньо використання дасть вам доступ до його методів, у тому числі poll"send_signal", "terminate" та "wait".

Ось Popenпідпис, наведений у джерелі . Я думаю, що це найточніша інкапсуляція інформації (на відміну від help(Popen)):

def __init__(self, args, bufsize=-1, executable=None,
             stdin=None, stdout=None, stderr=None,
             preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
             shell=False, cwd=None, env=None, universal_newlines=False,
             startupinfo=None, creationflags=0,
             restore_signals=True, start_new_session=False,
             pass_fds=(), *, encoding=None, errors=None):

Але більш інформативною є документація :Popen

subprocess.Popen(args, bufsize=-1, executable=None, stdin=None,
                 stdout=None, stderr=None, preexec_fn=None, close_fds=True,
                 shell=False, cwd=None, env=None, universal_newlines=False,
                 startupinfo=None, creationflags=0, restore_signals=True,
                 start_new_session=False, pass_fds=(), *, encoding=None, errors=None)

Виконайте дочірню програму в новому процесі. На POSIX клас використовує поведінку, подібну os.execvp (), для виконання дочірньої програми. У Windows клас використовує функцію Windows CreateProcess (). Аргументи Попена такі.

Розуміння решти документації Popenзалишатиметься вправою для читача.


Простий приклад двостороннього зв'язку між первинним процесом і подпроцесса можна знайти тут: stackoverflow.com/a/52841475/1349673
Джеймс Hirschorn

Перший приклад, мабуть, повинен мати shell=Trueабо (ще краще) передавати команду як список.
трійка

35

os.systemгаразд, але вид датований. Це також не дуже безпечно. Натомість спробуйте subprocess. subprocessне викликає sh безпосередньо і тому є більш безпечним, ніж os.system.

Дізнайтеся більше тут .


2
Хоча я погоджуюсь із загальною рекомендацією, subprocessвона не усуває всіх проблем із безпекою і має деякі набридливі проблеми.
трійчанка

33

Також є Plumbum

>>> from plumbum import local
>>> ls = local["ls"]
>>> ls
LocalCommand(<LocalPath /bin/ls>)
>>> ls()
u'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n'
>>> notepad = local["c:\\windows\\notepad.exe"]
>>> notepad()                                   # Notepad window pops up
u''                                             # Notepad window is closed by user, command returns

28

Використання:

import os

cmd = 'ls -al'

os.system(cmd)

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

Для отримання додаткових osфункцій, ось документація.


2
вона також застаріла. використовувати підпроцес
Corey Goldberg

28

Це може бути так просто:

import os
cmd = "your command"
os.system(cmd)

1
Це не дозволяє вказати на недоліки, які пояснюються набагато детальніше в PEP-324 . Документація для os.systemпрямого рекомендує уникати її на користь subprocess.
трійчанка

25

Мені дуже подобається shell_command за свою простоту. Він побудований поверх модуля підпроцесу.

Ось приклад з документації:

>>> from shell_command import shell_call
>>> shell_call("ls *.py")
setup.py  shell_command.py  test_shell_command.py
0
>>> shell_call("ls -l *.py")
-rw-r--r-- 1 ncoghlan ncoghlan  391 2011-12-11 12:07 setup.py
-rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py
-rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py
0

24

Тут є ще одна відмінність, про яку раніше не говорилося.

subprocess.Popenвиконує <command> як підпроцес. У моєму випадку мені потрібно виконати файл <a>, який потребує зв’язку з іншою програмою <b>.

Я спробував підпроцес, і виконання було успішним. Однак <b> не міг спілкуватися з <a>. Все нормально, коли я запускаю обоє з терміналу.

Ще одне: (ПРИМІТКА: kwrite поводиться відрізняється від інших програм. Якщо ви спробуєте нижче з Firefox, результати не будуть однаковими.)

Якщо ви спробуєте os.system("kwrite"), потік програми заморожується, поки користувач не закриє kwrite. Щоб подолати це, я намагався замість цього os.system(konsole -e kwrite). Цього разу програма продовжувала текти, але kwrite став підпроцесом консолі.

Будь-який запуск kwrite не є підпроцесом (тобто на системному моніторі він повинен з’являтися в крайньому лівому краю дерева).


1
Що ви маєте на увазі під "будь-хто запускає kwrite, не будучи підпроцесом" ?
Пітер Мортенсен

23

os.systemне дозволяє зберігати результати, тому якщо ви хочете зберегти результати в якомусь списку чи щось подібне, це subprocess.callпрацює.


22

subprocess.check_callзручно, якщо ви не хочете перевіряти значення повернення. Це викидає виняток на будь-яку помилку.


22

Я схильний використовувати підпроцес разом із shlex (для обробки виходу з цитованих рядків):

>>> import subprocess, shlex
>>> command = 'ls -l "/your/path/with spaces/"'
>>> call_params = shlex.split(command)
>>> print call_params
["ls", "-l", "/your/path/with spaces/"]
>>> subprocess.call(call_params)

17

Безсоромний штекер, я написав для цього бібліотеку: P https://github.com/houqp/shell.py

Це в основному обгортка для popen і shlex на даний момент. Він також підтримує команди конвеєра, щоб ви могли зручніше ланцюжок команд у Python. Тож ви можете робити такі речі, як:

ex('echo hello shell.py') | "awk '{print $2}'"

16

У Windows ви можете просто імпортувати subprocessмодуль та запускати зовнішні команди, зателефонувавши subprocess.Popen(), subprocess.Popen().communicate()і subprocess.Popen().wait()як це нижче:

# Python script to run a command line
import subprocess

def execute(cmd):
    """
        Purpose  : To execute a command and return exit status
        Argument : cmd - command to execute
        Return   : exit_code
    """
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    (result, error) = process.communicate()

    rc = process.wait()

    if rc != 0:
        print "Error: failed to execute command:", cmd
        print error
    return result
# def

command = "tasklist | grep python"
print "This process detail: \n", execute(command)

Вихід:

This process detail:
python.exe                     604 RDP-Tcp#0                  4      5,660 K

15

Під Linux, у випадку, якщо ви хочете викликати зовнішню команду, яка буде виконуватися незалежно (продовжуватиметься після закінчення сценарію python), ви можете використовувати просту чергу в якості спілера задач або в команді at

Приклад із спілером завдань:

import os
os.system('ts <your-command>')

Примітки про спілера завдань ( ts):

  1. Ви можете встановити кількість одночасних процесів, які слід запустити ("слоти") за допомогою:

    ts -S <number-of-slots>

  2. Встановлення tsне вимагає прав адміністратора. Ви можете завантажити і компілювати його з джерела просто make, додати його у свій шлях і все закінчено.


1
tsне є стандартним для будь-якого дистрибутива, про який я знаю, хоча вказівник на atце корисно. Напевно, ви також повинні згадати batch. Як і в інших місцях, os.system()рекомендація, мабуть, повинна принаймні згадувати, що subprocessце її рекомендована заміна.
трійка

15

Ви можете використовувати Popen, а потім можете перевірити стан процедури:

from subprocess import Popen

proc = Popen(['ls', '-l'])
if proc.poll() is None:
    proc.kill()

Перевірте підпроцес . Відкрийте .


15

Щоб отримати мережевий ідентифікатор з нейтрона OpenStack :

#!/usr/bin/python
import os
netid = "nova net-list | awk '/ External / { print $2 }'"
temp = os.popen(netid).read()  /* Here temp also contains new line (\n) */
networkId = temp.rstrip()
print(networkId)

Вихід нового net-list

+--------------------------------------+------------+------+
| ID                                   | Label      | CIDR |
+--------------------------------------+------------+------+
| 431c9014-5b5d-4b51-a357-66020ffbb123 | test1      | None |
| 27a74fcd-37c0-4789-9414-9531b7e3f126 | External   | None |
| 5a2712e9-70dc-4b0e-9281-17e02f4684c9 | management | None |
| 7aa697f5-0e60-4c15-b4cc-9cb659698512 | Internal   | None |
+--------------------------------------+------------+------+

Вихід друку (networkId)

27a74fcd-37c0-4789-9414-9531b7e3f126

Не варто рекомендувати os.popen()в 2016 році. Сценарій Awk можна було легко замінити нативним кодом Python.
трійка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.