Вызов внешней команды в 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 будет повторно вызвано после того, как дочерний процесс
прекращено.
Если проверка верна, и процесс завершается с ненулевым кодом выхода, a
Возникнет исключение 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
, указанная в source . Я думаю, что это наиболее точная инкапсуляция информации (в отличие от 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
будет оставлено читателю в качестве упражнения.