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


Як я можу викликати зовнішню команду (начебто я набрала його в командному рядку Unix або в командному рядку Windows) з скрипту Python?


3635
2017-09-18 01:35


походження




Відповіді:


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

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

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

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

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

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

Офіційна документація на підпроцесор модуль:


3504
2017-09-18 01:39



Чи є спосіб використання змінної заміни? IE я намагався зробити echo $PATH з допомогою call(["echo", "$PATH"]), але це просто повторювало літеральний рядок $PATH замість того, щоб робити будь-яку заміну. Я знаю, що міг би отримати змінну середовища PATH, але мені цікаво, чи є простий спосіб, щоб команда поводилася точно так, якби я виконував його в bash. - Kevin Wheeler
@ KevinWheeler Вам доведеться скористатись shell=True для того, щоб працювати. - SethMMorton
@ KevinWheeler Ви НЕ МАЮТЬ використовувати shell=True, для цього приходить Python os.path.expandvars. У вашому випадку ви можете написати: os.path.expandvars("$PATH"). @ SethMMorton, будь ласка, перегляньте свій коментар -> Чому б не використовувати shell = True - Murmel
Починаючи з Python 3.5, пропонується використовувати subprocess.run замість subprocess.call. docs.python.org/3/library/subprocess.html - Hannes Karppila
Приклад викликає ls -l але не дає доступу до його виходу (stdout не доступний). Я вважаю це заплутаним - ви можете використовувати команду без stdout замість цього, наприклад touch. - florisla


Нижче наведено короткий опис способів виклику зовнішніх програм та переваг та недоліків кожного з них:

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

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

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

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

  3. The 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 різних функцій Пена. Побачити документація.

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

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

    Побачити документація.

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

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

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

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

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

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


2469
2017-09-18 13:11



Красива відповідь / пояснення. Як ця відповідь виправдовує девіз Пітона, як це описано в цій статті? fastcompany.com/3026446/...  "Стилістично, Perl і Python мають різні філософії. Найбільш відомі девізи Perl:" Є більше, ніж один спосіб це зробити ". Пітон призначений для того, щоб мати один очевидний спосіб зробити це" Схоже, це має бути іншим шляхом! У Perl я знаю лише два способи виконання команди - використовуючи зворотний кліп або open. - Jean
Якщо використовуєте Python 3.5+, скористайтеся subprocess.run(). docs.python.org/3.5/library/subprocess.html#subprocess.run - phoenix
Що, як правило, потрібно знати, що робиться з STDOUT та STDERR у дочірньому процесі, тому що якщо вони ігноруються, то в деяких (досить поширених) умовах, в кінцевому підсумку, дочірнє процес викличе системний виклик, щоб написати STDOUT (також STDERR?). що перевищить вихідний буфер, передбачений для цього процесом ОС, і ОС призведе до блокування, доки який-небудь процес не буде прочитано з цього буфера. Отже, за допомогою рекомендованих зараз способів subprocess.run(..), що саме робить "За промовчанням це не фіксує stdout або stderr". мати на увазі? А як на рахунок subprocess.check_output(..) і STDERR? - Evgeni Sergeev
який з команд ви рекомендували заблокувати мій скрипт? Тобто, якщо я хочу запустити кілька команд у a forцикл як це зробити, не заблокуючи мого python-скрипта? Я не дбаю про вихід команди, я просто хочу запустити багато з них. - Charlie Parker
@Фенойк я не погоджуюсь. Ніщо не заважає вам використовувати os.system у python3 docs.python.org/3/library/os.html#os.system - Qback


Я зазвичай використовую:

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().


257
2017-09-18 18:20



.readlines() читає все лінії відразу, тобто він блокується, поки не закінчиться підпроцесор (закривається його кінець труби). Читати в режимі реального часу (якщо немає проблем із буферизацією), ви можете: for line in iter(p.stdout.readline, ''): print line, - jfs
Не могли б ви розібратися в тому, що ви маєте на увазі під "якщо немає питань буферизації"? Якщо процес певним чином блокується, виклик підпроцесу також блокується. Те ж саме може статися і з моїм оригінальним прикладом. Що ще може статися щодо буферизації? - EmmEff
дочірній процес може використовувати блок-буферизацію в неінтерактивному режимі замість буферизації рядків так p.stdout.readline() (примітка: № s в кінці) не побачить жодних даних, поки дитина не заповнить його буфер. Якщо дитина не виробляє багато даних, то виведення не буде в режимі реального часу. Див другу причину в Питання: чому б просто не використовувати трубу (popen ())?. Деякі обхідні шляхи передбачені у цьому відповіді (pexpect, pty, stdbuf) - jfs
питання буферизації має значення, якщо ви хочете отримати вихід у реальному часі і не застосовується до вашого коду, який нічого не друкує все дані отримані - jfs


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

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

Класичний приклад із підпроцесу модуль docs:

import subprocess
import sys

# some code here

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

# some more code here

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

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

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

Рішення - пройти DETACHED_PROCESS Прапор створення процесу до основної функції CreateProcess у win API. Якщо вам доведеться встановити 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. Якщо хтось знає, поділіться своїми ідеями. Пошук Google при запуску фонових процесів у Python ще не проливає світла.


158
2018-02-12 10:15



Я помітив можливий "примха" при розробці py2exe-додатків в pytet + eclipse. я зміг сказати, що основний скрипт не був відірваний, оскільки вікно виводу Eclipse не закінчилося; навіть якщо скрипт виконує до кінця, він все ще чекає повернення. але коли я намагався компілювати до виконуваного файлу py2exe, відбувається очікувана поведінка (працює процеси як окремі, а потім закривається). Я не впевнений, але виконуване ім'я більше не входить до списку процесів. це працює для всіх підходів (os.system ("start *"), os.spawnl з os.P_DETACH, subprocs і т.д.) - maranas
Windows gotcha: хоча я породив процес з DETACHED_PROCESS, коли я вбив свій демон Python, всі відкриті ним порти не звільняються, поки всі процеси, що породжуються, не припиняться. WScript.Shell вирішив всі мої проблеми. Приклад тут: pastebin.com/xGmuvwSx - Alexey Lebedev
вам також може знадобитися прапорець CREATE_NEW_PROCESS_GROUP. Побачити Попен чекає дитину, навіть коли перерується дитина - jfs
Неправильно вказано: "[o] n windows (win xp), батьківський процес не закінчиться, поки longtask.py не завершить роботу". Вихід батька буде нормально, але вікно консолі (консольний екземпляр conhost.exe) закривається лише тоді, коли завершується останній доданий процес, і дитина, можливо, успадкувала консоль батьків. Налаштування DETACHED_PROCESS в creationflags уникає цього, не дозволяючи дитині наслідувати або створювати консоль. Якщо ви замість цього хочете нову консоль, скористайтеся CREATE_NEW_CONSOLE (0x00000010). - eryksun
Я не мав на увазі, що виконання як окремого процесу є невірним. Тим не менш, вам може знадобитися встановити стандартні ручки для файлів, труб або os.devnull тому що деякі консольні програми виходять з помилкою в іншому випадку. Створіть нову консоль, коли ви хочете, щоб дочірній процес взаємодіяти з користувачем одночасно з батьківським процесом. Було б незрозуміло спробувати зробити це в одному вікні. - eryksun


Я рекомендую використовувати модуль підпроцесації замість os.system, оскільки він вичерпується для вас, і тому набагато безпечніше: http://docs.python.org/library/subprocess.html

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

98
2017-09-18 01:42





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

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


94
2017-09-18 01:37



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


import os
os.system("your command")

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


84
2017-09-18 01:37



Що ти маєш на увазі "команда не очищена"? - Peter Mortensen
Немає поняття, про що я мав на увазі майже десять років тому (перевірте дату!), Але, якщо мені доведеться здогадатися, це не означає, що це не було перевірено. - nimish


Я завжди користуюся 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)

52
2018-03-13 00:12





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

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

child = pexpect.spawn('ftp 192.168.0.24')

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

child.sendline('anonymous')

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

52
2017-10-07 07:09





Є багато різних бібліотек, які дозволяють викликати зовнішні команди за допомогою 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). ОС завжди буде запускати оболонку і є простою альтернативою для людей, яким не потрібно або які не вміють користуватися 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

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

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

Пекінг

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 це краща альтернатива.

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


48
2017-10-29 14:02





Якщо вам потрібен вихід із команди, яку ви телефонуєте, то можна використовувати 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)


45
2018-04-28 20:29



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