Автор выбрал COVID-19 Relief Fund для получения пожертвования в рамках программы Write for DOnations.
Python 3 включает модуль subprocess
для запуска внешних программ и чтения их выводов в коде Python.
Модуль subprocess
может оказаться вам полезен, если вы хотите использовать на своем компьютере другую программу из вашего кода Python. Например, вы можете запустить git
из своего кода Python для извлечения в ваш проект, отслеживаемый в системе управления версиями git
. Поскольку с помощью модуля subprocess
можно контролировать любую программу, доступную на вашем компьютере, приведенные здесь примеры применимы к любым внешним программам, которые вы можете запустить из своего кода Python.
subprocess
содержит несколько классов и функций, но в этом обучающем модуле мы расскажем об одной из самых полезных функций модуля subprocess
: subprocess.run
. Мы изучим разные схемы его применения и основные аргументы ключевых слов.
Чтобы получить максимум знаний из этого обучающего модуля, рекомендуется иметь небольшое представление о программировании на Python 3. Дополнительную информацию можно найти в следующих обучающих руководствах:
Теперь вы можете использовать функцию subprocess.run
для запуска внешней программы с кода Python. Прежде всего необходимо импортировать в программу модули subprocess
и sys
:
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "print('ocean')"])
Если вы запустите их, вы увидите следующий вывод:
Outputocean
Рассмотрим этот пример:
sys.executable
— абсолютный путь к исполняемому файлу Python, из которого изначально была запущена ваша программа. Например, sys.executable
может быть путем, таким как /usr/local/bin/python
.subprocess.run
получает список строк, состоящих из компонентов команды, которую мы пытаемся запустить. Поскольку первой строкой мы передаем sys.executable
, мы предписываем subprocess.run
запустить новую программу Python.-c
— это опция командной строки python
, позволяющая передать строку с целой программой Python для исполнения. В нашем случае мы передаем программу, которая распечатывает строку ocean
.Вы можете представить каждую запись в списке, отправляемом в subprocess.run
, как отделенную пробелом. Например, [sys.executable, "-c", "print('ocean')"]
примерно эквивалентно /usr/local/bin/python -c "print('ocean')"
. Обратите внимание, что subprocess
автоматически заключает в кавычки компоненты команды перед их запуском в операционной системе, так что вы можете передать имя файла с пробелами.
Предупреждение. Никогда не передавайте в subprocess.run
строки из ненадежных источников. Поскольку subprocess.run
может выполнять на компьютере произвольные команды, злоумышленники могут использовать его для манипуляции компьютером непредвиденным образом.
Теперь мы можем вызывать внешнюю программу subprocess.run
. Далее мы покажем, как захватывать вывод этой программы. Например, этот процесс может быть полезным, если бы мы хотели использовать файлы git ls
для вывода всех файлов, хранящихся в системе контроля версий.
Примечание. Для примеров, показанных в этом разделе, требуется версия Python 3.7 или выше. В частности, в версию Python 3.7, выпущенную в июне 2018 г. были добавлены аргументы capture_output
и ключевое слово text
.
Дополним наш предыдущий пример:
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "print('ocean')"], capture_output=True, text=True
)
print("stdout:", result.stdout)
print("stderr:", result.stderr)
Если запустить этот код, результат будет выглядеть следующим образом:
Outputstdout: ocean
stderr:
Этот пример в целом аналогичен рассмотренному в первом разделе, и мы все еще запускаем субпроцесс для печати ocean
. Однако теперь мы передаем аргументы ключевых слов capture_output=True
и text=True
в subprocess.run
.
subprocess.run
возвращает объект subprocess.CompletedProcess
, который привязан к результату
. Объект subprocess.CompletedProcess
содержит детали о коде выхода внешней программы и ее выводе. Благодаря capture_output=True
в result.stdout
и result.stderr
вносится соответствующий вывод внешней программы. По умолчанию result.stdout
и result.stderr
привязаны как байты, но аргумент ключевого слова text=True
предписывает Python декодировать байты в строки.
В разделе вывода stdout
— это ocean
(плюс новая строка в конце, где print
добавляет явность), и у нас нет stderr
.
Рассмотрим пример, где значение stderr
не пустое:
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "raise ValueError('oops')"], capture_output=True, text=True
)
print("stdout:", result.stdout)
print("stderr:", result.stderr)
Если запустить этот код, вывод будет выглядеть следующим образом:
Outputstdout:
stderr: Traceback (most recent call last):
File "<string>", line 1, in <module>
ValueError: oops
Этот код запускает субпроцесс Python, который немедленно выдает ошибку ValueError
. При просмотре окончательного результата
мы не видим ничего в stdout
и видим результат Traceback
ошибки ValueError
в stderr
. Это связано с тем, что по умолчанию Python записывает Traceback
необработанного исключения в stderr
.
Иногда бывает полезно создать исключение, если при выходе из работающей программы выдается код ошибки выхода. Программы, выдающие нулевой код ошибки при выходе считаются успешными, однако при ненулевом коде ошибки выхода считается, что в программе произошла ошибка. Например, данная система может быть полезной, если нам нужно выдавать исключение при запуске файлов git ls
в каталоге, не являющемся репозиторием git
.
Мы можем использовать аргумент ключевого слова check=True
, чтобы subprocess.run
выдавал исключение в случае возврата внешней программой ненулевого кода ошибки выхода:
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"], check=True)
Если запустить этот код, вывод будет выглядеть следующим образом:
OutputTraceback (most recent call last):
File "<string>", line 1, in <module>
ValueError: oops
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/subprocess.py", line 512, in run
raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.
Этот вывод показывает, что мы запустили субпроцесс, выдавший ошибку, которая выводится в stderr
на нашем терминале. Затем subprocess.run
добросовестно выдал ошибку subprocess.CalledProcessError
от нашего имени в нашей основной программе Python.
Также модуль subprocess
включает метод subprocess.CompletedProcess.check_returncode
, который можно использовать с похожим результатом:
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"])
result.check_returncode()
Если мы запустим этот код, мы получим:
OutputTraceback (most recent call last):
File "<string>", line 1, in <module>
ValueError: oops
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/subprocess.py", line 444, in check_returncode
raise CalledProcessError(self.returncode, self.args, self.stdout,
subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.
Поскольку мы не передали check=True
в subprocess.run
, мы успешно привязали экземпляр subprocess.CompletedProcess
к результату
, хотя наша программа выполнила выход с ненулевым кодом ошибки. При этом при вызове result.check_returncode()
выдается ошибка subprocess.CalledProcessError
, поскольку модуль обнаруживает код ошибки выхода для завершенного процесса.
subprocess.run
имеет аргумент timeout
, позволяющий остановить внешнюю программу, если ее выполнение занимает слишком много времени:
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "import time; time.sleep(2)"], timeout=1)
Если запустить этот код, результат будет выглядеть следующим образом:
OutputTraceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/subprocess.py", line 491, in run
stdout, stderr = process.communicate(input, timeout=timeout)
File "/usr/local/lib/python3.8/subprocess.py", line 1024, in communicate
stdout, stderr = self._communicate(input, endtime, timeout)
File "/usr/local/lib/python3.8/subprocess.py", line 1892, in _communicate
self.wait(timeout=self._remaining_time(endtime))
File "/usr/local/lib/python3.8/subprocess.py", line 1079, in wait
return self._wait(timeout=timeout)
File "/usr/local/lib/python3.8/subprocess.py", line 1796, in _wait
raise TimeoutExpired(self.args, timeout)
subprocess.TimeoutExpired: Command '['/usr/local/bin/python', '-c', 'import time; time.sleep(2)']' timed out after 0.9997982999999522 seconds
Субпроцесс, который мы попытались запустить, использовал функцию time.sleep
для сна в течение 2
секунд. Однако мы передали аргумент с ключевым словом timeout=1
в subprocess.run
, чтобы закрыть наш субпроцесс по таймауту по прошествии 1
секунды. Это объясняет, почему при вызове subprocess.run
в конечном итоге было выдано исключение subprocess.TimeoutExpired
.
Обратите внимание, что аргумент ключевого слова timeout
для subprocess.run
является приблизительным. Python постарается остановить субпроцесс после заданного в аргументе timeout
количества секунд, но это не обязательно будет точным.
Иногда программы ожидают передачи ввода через stdin
.
Аргумент ключевого слова input
для subprocess.run
позволяет передавать данные в stdin
субпроцесса. Например:
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "import sys; print(sys.stdin.read())"], input=b"underwater"
)
Запустив этот код, мы получим примерно следующий вывод:
Outputunderwater
В данном случае мы передали байты underwater
в input
. Наш целевой субпроцесс использовал sys.stdin
для чтения переданного в stdin
аргумента (underwater
) и вывел его в результатах.
Аргумент ключевого слова input
может быть полезен, если вы хотите создать цепочку из нескольких вызовов subprocess.run
с передачей вывода одной программы как ввода в другую программу.
Модуль subprocess
— это мощный компонент стандартной библиотеки Python, позволяющий легко запускать внешние программы и просматривать их вывод. В этом обучающем модуле вы научились использовать subprocess.run
для управления внешними программами, передачи ввода в эти программы, анализа их вывода и проверки возвращаемых ими кодов.
Модуль subprocess
представляет дополнительные классы и утилиты, которые мы не охватили в этом обучающем модуле. Изучив основы, вы можете использовать документацию модуля subprocess
для получения дополнительной информации о других имеющихся классах и утилитах.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!