Как вы вызываете внешнюю команду (как будто я набрал ее в оболочке Unix или командной строке Windows) из скрипта Python?
27 ответов
Посмотрите на модуль подпроцесса в стандартной библиотеке:
import subprocess
subprocess.run(["ls", "-l"])
Преимущество subprocess
против system
заключается в том, что он более гибкий (вы можете получить stdout
, stderr
, «реальный» код состояния, лучшую обработку ошибок и т. Д. ... ) .
официальная документация рекомендует модуль subprocess
вместо альтернативного os.system()
:
Модуль
subprocess
предоставляет более мощные средства для порождения новых процессов и получения их результатов; использование этого модуля предпочтительнее, чем использование этой функции [os.system()
] .
Для версий Python до 3.5 используйте call
:
import subprocess
subprocess.call(["ls", "-l"])
Я склонен использовать подпроцесс вместе с 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)
subprocess.check_call
удобно, если вы не хотите проверять возвращаемые значения. Выдает исключение при любой ошибке.
Некоторые советы по отсоединению дочернего процесса от вызывающего (запуск дочернего процесса в фоновом режиме).
Предположим, вы хотите запустить длинную задачу из скрипта 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 пока не проливает свет.
Со стандартной библиотекой
Используйте модуль подпроцесса (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
.
Если вам нужен вывод команды, которую вы вызываете, тогда вы можете использовать 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 } } ) .
Это может быть так просто:
import os
cmd = "your command"
os.system(cmd)
Вызов внешней команды в 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
останется в качестве упражнения для читателя.
Я бы рекомендовал использовать модуль подпроцесс вместо os.system, потому что он выполняет экранирование оболочки для вас и поэтому намного безопаснее.
subprocess.call(['ping', 'localhost'])
В 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
Использование:
import os
cmd = 'ls -al'
os.system(cmd)
os - этот модуль предоставляет переносимый способ использования функций, зависящих от операционной системы.
Для других os
функций здесь - документация.
Вот как я выполняю свои команды. Этот код имеет все, что вам нужно в значительной степени
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()
Существует также 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
Проверьте также библиотеку Python "pexpect".
Он позволяет интерактивно управлять внешними программами / командами, даже ssh, ftp, telnet и т. Д. Вы можете просто напечатать что-то вроде:
child = pexpect.spawn('ftp 192.168.0.24')
child.expect('(?i)name .*: ')
child.sendline('anonymous')
child.expect('(?i)password')
Я всегда использую 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)
import os
cmd = 'ls -al'
os.system(cmd)
Если вы хотите вернуть результаты команды, вы можете использовать {{X0 } }. Однако, начиная с версии 2.6, это не рекомендуется в пользу модуля подпроцесса , которые другие ответы хорошо освещены.
Обновление:
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'>]
Мне очень нравится 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
Бесстыдная заглушка, я для этого написал библиотеку: P https://github.com/houqp/shell.py
Это в основном обертка для попен и шлекс на данный момент. Он также поддерживает команды конвейеризации, так что вы можете упростить цепочку команд в Python. Таким образом, вы можете делать такие вещи, как:
ex('echo hello shell.py') | "awk '{print $2}'"
import os
os.system("your command")
Обратите внимание, что это опасно, так как команда не очищена. Я оставляю это на ваше усмотрение в Google для получения соответствующей документации по модулям 'os' и 'sys'. Существует множество функций (exec * и spawn *), которые будут выполнять похожие действия.
Используйте модуль os:
import os
os.system("your command")
Например,
import os
os.system("ifconfig")
Вы можете использовать Popen, а затем вы можете проверить статус процедуры:
from subprocess import Popen
proc = Popen(['ls', '-l'])
if proc.poll() is None:
proc.kill()
Ознакомьтесь с subprocess.Popen.
os.system
в порядке, но немного устаревший. Это также не очень безопасно. Вместо этого попробуйте subprocess
. subprocess
не вызывает sh напрямую и поэтому более безопасен, чем os.system
.
Получить дополнительную информацию здесь.
Типичная реализация:
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()
.
Используйте подпроцесс .
... или для очень простой команды:
import os
os.system('cat testfile')
os.system
не позволяет вам сохранять результаты, поэтому, если вы хотите сохранить результаты в каком-то списке или чем-то, работает subprocess.call
.
Без вывода результата:
import os
os.system("your command here")
С выводом результата:
import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")
Похожие вопросы
Новые вопросы
python
Python - это многопарадигмальный, динамически типизированный, многоцелевой язык программирования. Он разработан для быстрого изучения, понимания и использования, а также для обеспечения чистого и единообразного синтаксиса. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Тем не менее, для вопросов о Python, связанных с версией, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas и NumPy) включите его в теги.