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

4739
freshWoWer 18 Сен 2008 в 05:35

27 ответов

Лучший ответ

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

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

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

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

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

Замена старых функций модулем подпроцесса < Раздел / a> в документации subprocess может содержать несколько полезных рецептов.

Для версий Python до 3.5 используйте call:

import subprocess
subprocess.call(["ls", "-l"])
4552
Corey Goldberg 31 Авг 2019 в 03:17

Я склонен использовать подпроцесс вместе с 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)
21
Emil Stenström 30 Апр 2014 в 14:37

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

21
cdunn2001 18 Янв 2011 в 19:21

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

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

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

import subprocess
import sys

# Some code here

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

# Some more code here

Идея здесь в том, что вы не хотите ждать в строке 'call subprocess', пока longtask.py не будет завершен. Но не ясно, что происходит после строки «еще немного кода здесь» из примера.

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

В Windows (Windows XP) родительский процесс не завершится, пока longtask.py не завершит свою работу. Это не то, что вы хотите в CGI-скрипте. Проблема не является специфичной для Python; в сообществе PHP проблемы одинаковы.

Решение состоит в том, чтобы передать DETACHED_PROCESS Создание процесса Отметьте для базовой функции CreateProcess в Windows 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 пока не проливает свет.

220
Peter Mortensen 29 Ноя 2019 в 21:46

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

Используйте модуль подпроцесса (Python 3):

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

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

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

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

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

С внешними зависимостями

Если вы не против внешних зависимостей, используйте plumbum:

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

Это лучшая subprocess оболочка. Он кроссплатформенный, то есть работает как в Windows, так и в Unix-подобных системах. Установить с помощью pip install plumbum.

Еще одна популярная библиотека sh:

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

Однако sh отказался от поддержки Windows, так что это не так круто, как раньше. Установить с помощью pip install sh.

67
6 revs, 2 users 79% 29 Ноя 2019 в 21:54

Если вам нужен вывод команды, которую вы вызываете, тогда вы можете использовать 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'

Также обратите внимание на параметр shell.

Если shell True, указанная команда будет выполнена через оболочку. Это может быть полезно, если вы используете Python в первую очередь для расширенного потока управления, который он предлагает для большинства системных оболочек, и все еще хотите иметь удобный доступ к другим функциям оболочки, таким как каналы оболочки, подстановочные знаки имени файла, расширение переменных среды и расширение ~ до дома пользователя. каталог . Однако обратите внимание, что сам Python предлагает реализации многих функций, подобных оболочке (в частности, glob, fnmatch, os.walk(), os.path.expandvars(), os.path.expanduser() и { { Х6 } } ) .

67
Peter Mortensen 3 Июн 2018 в 20:18

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

import os
cmd = "your command"
os.system(cmd)
27
Samadi Salahedine 8 Июн 2018 в 12:06

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

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

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

Если проверка имеет значение true, и процесс завершается с ненулевым кодом завершения, будет вызвано исключение 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? Я бы изо всех сил пытался найти вариант использования, основанный только на аргументах Однако прямое использование 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 останется в качестве упражнения для читателя.

34
Aaron Hall 18 Окт 2017 в 16:37

Я бы рекомендовал использовать модуль подпроцесс вместо os.system, потому что он выполняет экранирование оболочки для вас и поэтому намного безопаснее.

subprocess.call(['ping', 'localhost'])
139
Nicolas Gervais 24 Фев 2020 в 01:01

В 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
16
Peter Mortensen 28 Май 2017 в 23:08

Использование:

import os

cmd = 'ls -al'

os.system(cmd)

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

Для других os функций здесь - документация.

27
Peter Mortensen 28 Май 2017 в 23:05

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

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()
54
Usman Khan 28 Окт 2012 в 05:44

Существует также 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
32
stuckintheshuck 10 Окт 2014 в 17:41

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

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

child = pexpect.spawn('ftp 192.168.0.24')

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

child.sendline('anonymous')

child.expect('(?i)password')
71
Peter Mortensen 28 Май 2017 в 23:02

Я всегда использую 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)
71
Peter Mortensen 29 Ноя 2019 в 21:47
import os
cmd = 'ls -al'
os.system(cmd)

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

136
Patrick M 26 Янв 2016 в 16:53

Обновление:

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'>]
52
Peter Mortensen 29 Ноя 2019 в 21:52

Мне очень нравится 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
23
Peter Mortensen 29 Ноя 2019 в 21:49

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

Это в основном обертка для попен и шлекс на данный момент. Он также поддерживает команды конвейеризации, так что вы можете упростить цепочку команд в Python. Таким образом, вы можете делать такие вещи, как:

ex('echo hello shell.py') | "awk '{print $2}'"
16
houqp 1 Май 2014 в 20:49
import os
os.system("your command")

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

139
Peter Mortensen 3 Июн 2018 в 17:10

Используйте модуль os:

import os
os.system("your command")

Например,

import os
os.system("ifconfig")
25
Peter Mortensen 29 Ноя 2019 в 22:09

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

from subprocess import Popen

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

Ознакомьтесь с subprocess.Popen.

15
Peter Mortensen 28 Май 2017 в 23:01

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

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

34
Dimitris Fasarakis Hilliard 10 Дек 2016 в 13:25

Типичная реализация:

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

344
Trenton McKinney 4 Окт 2019 в 02:33

Используйте подпроцесс .

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

import os
os.system('cat testfile')
39
Peter Mortensen 29 Ноя 2019 в 21:39

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

21
Peter Mortensen 29 Ноя 2019 в 21:48

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

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

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

import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")
42
Peter Mortensen 28 Май 2017 в 22:59