Вопрос

Как я могу вызвать внешнюю команду (как если бы я набрал ее в оболочке Unix или командной строке Windows) изнутри Python сценарий?

Это было полезно?

Решение

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

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

Преимущество подпроцесс против. система заключается в том, что он более гибок (вы можете получить стандартный вывод, стандартный вывод, «настоящий» код состояния, лучшую обработку ошибок и т. д.).

А официальная документация рекомендует подпроцесс модуль вместо альтернативы os.system():

А подпроцесс модуль предоставляет более мощные возможности для создания новых процессов и получения их результатов;использование этого модуля предпочтительнее использования этой функции [os.system()].

"Замена старых функций модулем подпроцесса"раздел в подпроцесс в документации могут быть некоторые полезные рецепты.

Более старые версии Python используют вызов:

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

Другие советы

Вот краткий обзор способов вызова внешних программ, а также преимуществ и недостатков каждого из них:

  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. А Popen класс subprocess модуль.Это предназначено в качестве замены os.popen но у него есть обратная сторона: он немного сложнее из-за своей полноты.Например, вы бы сказали:

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

    вместо:

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

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

  4. А call функция от subprocess модуль.Это в основном так же, как Popen class и принимает все те же аргументы, но просто ждет завершения команды и возвращает код возврата.Например:

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

    Видеть документация.

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

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

А subprocess модуль, вероятно, должен быть тем, что вы используете.

Наконец, имейте в виду, что все методы, в которых вы передаете последнюю команду, должны выполняться оболочкой в ​​виде строки, и вы несете ответственность за ее экранирование. Существуют серьезные последствия для безопасности если какой-либо части передаваемой вами строки нельзя полностью доверять.Например, если пользователь вводит некоторую/любую часть строки.Если вы не уверены, используйте эти методы только с константами.Чтобы дать вам представление о последствиях, рассмотрим этот код:

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

и представьте, что пользователь вводит что-то «моя мама меня не любила && rm -rf /», что может стереть всю файловую систему.

Обычно я использую:

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

Несколько советов по отделению дочернего процесса от вызывающего (запуск дочернего процесса в фоновом режиме).

Предположим, вы хотите запустить длинную задачу из 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 (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

/* УПД 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 пока не пролил никакого света.

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

subprocess.call(['ping', 'localhost'])
import os
cmd = 'ls -al'
os.system(cmd)

Если вы хотите вернуть результаты команды, вы можете использовать os.popen.Однако с версии 2.6 это устарело в пользу модуль подпроцесса, что хорошо описано в других ответах.

import os
os.system("your command")

Обратите внимание, что это опасно, поскольку команда не очищается.Я оставляю вам возможность поискать в Google соответствующую документацию по модулям «os» и «sys».Существует множество функций (exec* и spawn*), которые делают аналогичные вещи.

Существует множество различных библиотек, которые позволяют вызывать внешние команды с помощью 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 (Примечание:Также существует subprocess.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.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')

ткань

Fabric — это библиотека Python 2.5 и 2.7.Он позволяет выполнять локальные и удаленные команды оболочки.Fabric — простая альтернатива запуску команд в защищенной оболочке (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 это лучшая альтернатива.

Редактирование было основано на книге Дж.Ф.Комментарий Себастьяна.

Я всегда использую 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)

Проверьте также библиотеку Python «pexpect».

Он позволяет интерактивно управлять внешними программами/командами, даже ssh, ftp, telnet и т. д.Вы можете просто ввести что-то вроде:

child = pexpect.spawn('ftp 192.168.0.24')

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

child.sendline('anonymous')

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

Если вам нужен вывод из команды, которую вы звоните, вы можете использовать subprocess.check_output (Питон 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).

Со стандартной библиотекой

Использование модуль подпроцесса (Питон 3):

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

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

Примечание по версии Python:Если вы все еще используете Python 2, подпроцесс.вызов работает аналогичным образом.

Профессиональный совет: 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.

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

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

Обновлять:

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'>]

Без вывода результата:

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

С выводом результата:

import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")

https://docs.python.org/2/library/subprocess.html

...или для очень простой команды:

import os
os.system('cat testfile')

А также есть Свинец

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

os.system нормально, но немного устаревшее.Это также не очень безопасно.Вместо этого попробуйте subprocess. subprocess не вызывает sh напрямую и поэтому более безопасен, чем os.system.

Получить дополнительную информацию здесь.

Вызов внешней команды в 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, документация рекомендует подпроцесс.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, а не в стандартный вывод.)

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

Можно легко перейти от ручного предоставления командной строки (как предполагает вопрос) к предоставлению строки, созданной программно. Не создавайте строки программно. Это потенциальная проблема безопасности.Лучше предположить, что вы не доверяете вводным данным.

>>> 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), который будет передан на стандартный ввод подпроцесса.

В документации описывается timeout= и check=True лучше, чем я мог бы:

Аргумент тайм-аута передается в Popen.communicate().Если истекает тайм -аут, дочерний процесс будет убит и ждат.Исключение TimeAtExpired будет повторно затронуто после прекращения детского процесса.

Если проверка верна, и процесс выходит с кода выхода из ненулевого выхода, будет поднято исключение, называемое processerror.Атрибуты этого исключения держат аргументы, код выхода, а также 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», «завершить» и «подождать».

Вот 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 оставим в качестве упражнения для читателя.

Использовать:

import os

cmd = 'ls -al'

os.system(cmd)

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

Для более os функции, здесь это документация.

Это может быть так просто:

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

используйте модуль ОС

import os
os.system("your command")

например

import os
os.system("ifconfig")

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

Я склонен использовать подпроцесс вместе с шлекс (для обработки экранирования строк в кавычках):

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

Здесь есть еще одно отличие, о котором ранее не упоминалось.

subprocess.Popen выполняет <команду> как подпроцесс.В моем случае мне нужно выполнить файл <a>, который должен взаимодействовать с другой программой <b>.

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

Еще один:(ПРИМЕЧАНИЕ:kwrite ведет себя иначе, чем другие приложения.Если вы попробуете выполнить приведенное ниже действие с помощью Firefox, результаты не будут такими же.)

Если вы попытаетесь os.system("kwrite"), выполнение программы зависает до тех пор, пока пользователь не закроет kwrite.Чтобы преодолеть это, я попытался вместо этого os.system(konsole -e kwrite).На этот раз программа продолжала работать, но kwrite стал подпроцессом консоли.

Любой, кто запускает kwrite, не является подпроцессом (т.на системном мониторе он должен появиться на крайнем левом краю дерева).

os.system не позволяет сохранять результаты, поэтому, если вы хотите сохранить результаты в каком-то списке или что-то в этом роде subprocess.call работает.

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

Вот пример из документации:

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

Бесстыдный плагин, я написал для этого библиотеку :Phttps://github.com/houqp/shell.py

По сути, на данный момент это оболочка для popen и shlex.Он также поддерживает команды конвейерной обработки, что позволяет упростить цепочку команд в Python.Итак, вы можете делать такие вещи, как:

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

В Linux, если вы хотите вызвать внешнюю команду, которая будет выполняться независимо (продолжит работать после завершения сценария Python), вы можете использовать простую очередь как диспетчер очереди задач или в команда

Пример с диспетчером очереди задач:

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

Примечания о диспетчере очереди задач (ts):

  1. Вы можете установить количество одновременно запускаемых процессов («слотов») с помощью:

    ts -S <number-of-slots>

  2. Установка ts не требует прав администратора.Вы можете скачать и скомпилировать его из исходного кода с помощью простой make, добавьте его в свой путь, и все готово.

Вы можете использовать Popen, а затем проверить статус процедуры:

from subprocess import Popen

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

Проверить подпроцесс.Popen.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top