В питоне есть модуль subprocess который предоставляет средства запуска внешних процессов и выполнения команд оболочки.
Для выполнения некоторых команд без необходимости перехвата их вывода можно использовать функцию run.
1 2 3 4 |
import subprocess result = subprocess.run(['dir','c:\\'], shell=True) print(result.returncode); |
В результате выполнения кода выше будет выведен статус возврата команды оповещающий о результате ее выполнения (с ошибкой или без). Стоит также заметить, что здесь использован параметр shell, который указывает, что перед запуском команды необходимо сначала загрузить командную оболочку, а уже из нее выполнить команду. Это необходимо в тех случаях, когда передаваемая на выполнения команда содержит функции оболочки. В результате вызова run возвращается объект CompletedProcess, который и содержит информацию о коде возврата и выводе команды. Еще одна особенность run, это ее атрибут check, который указывает, на необходимость проверки статуса возврата. Если check=True, и статус возврата будет указывать на ошибку, то будет возбужденно исключение.
Примечание: вызов run без параметра shell полностью эквивалентен вызову функции call(), которая возвращает только код возврата. Передача аргумента check=True, создает из run, полный аналог функции check_call().
Аналогично с командой оболочки работает и функция os.system, которой в качестве параметра передается вся команда которая должна быть выполнена вместе с параметрами.
В случае необходимости получения потока вывода, программы или команды, необходимо задать функции параметр stdout:
1 2 3 4 5 |
import subprocess result = subprocess.run(['dir','c:\\'], shell=True, stdout=subprocess.PIPE) print(result.returncode); print(result.stdout.decode('cp866')) |
Аналогичное поведение имеет и более низкоуровневая функция popen из модуля os:
1 2 3 4 |
import os print(os.system('dir c:\\')) print(os.popen('dir c:\\').read()) |
Примечание: вызов run с check=True и stdout=subprocess.PIPE, создает из run, полный аналог функции check_output().
Стоит также отметить, что если программа будет писать данные в поток ошибок STDERR, то для его перехвата надо установить значение атрибута stderr в значение PIPE.
Если вывод команды нужно подавить, то тогда вместо PIPE надо указать subprocess.DEVNULL.
Класс Popen
Класс Popen является оболочкой вокруг функции os.popen. Для получения вывода используется класс которому коме команды передается также атрибут stdout:
1 2 3 4 5 |
import subprocess result = subprocess.Popen(['dir','c:\\'], shell=True, stdout=subprocess.PIPE) text = result.communicate()[0].decode('cp866') print(text) |
Здесь мы связали поток стандартного вывода команды оболочки с каналом и вызвали метод communicate, ожидающий завершения команды и принимающий текст, который она выводит в стандартный поток вывода и в стандартный поток ошибок. Код завершения команды доступен в виде атрибута, после того как она будет выполнена. Это полный аналог функции popen стой лишь разницей, что выводом управляет экземпляр Popen.
Точно так же мы могли бы использовать отдельную функцию чтения потока стандартного вывода команды и отдельную функцию ожидания ее завершения (которая возвращает код завершения):
1 2 3 4 |
import subprocess result = subprocess.Popen(['dir','c:\\'], shell=True, stdout=subprocess.PIPE) print(result.stdout.read()) result.wait() |
Передача потока в программу
Что бы выполнить запись Popen надо настроить следующим образом:
1 2 3 |
import subprocess result = subprocess.Popen(['cat','-'], stdin=subprocess.PIPE) result.communicate('data'.encode('utf-8')) |
Что бы отправить данные в канал их требуется передать методы communicate.
Возможен вариант двухстороннего взаимодействия:
1 2 3 4 |
import subprocess result = subprocess.Popen(['cat','-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE) text = result.communicate('data'.encode('utf-8'))[0].decode('utf-8') print(text) |
Перехват всех трех каналов ввода-вывода (ввод, вывод и канал ошибок), делается вызовом похожим на вызов popen3():
1 2 3 4 5 |
import subprocess result = subprocess.Popen(['cat','-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) text, err = result.communicate('data'.encode('utf-8')) print(text.decode('utf-8')) print(err.decode('utf-8')) |
Иногда необходимо объединить вывод stdout и stderr, что можно сделать командой:
1 2 3 4 5 |
import subprocess result = subprocess.Popen('cat -; echo "to stderr" 1>&2', stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) text, err = result.communicate('data'.encode('utf-8')) print('Объединенный вывод',text.decode('utf-8')) print('Тут будет пусто',err.decode('utf-8')) |
ПРИМЕЧАНИЕ: Инструкция на языке Python exec(open(file).read()) тоже выполняет код программного файла, но внутри того же процесса, который ее вызвал. В этом отношении она похожа на операцию импортирования, но по своему действию она больше похожа на операцию вставки содержимого файла в вызывающую программу в том месте, где стоит вызов exec (если явно не передаются словари глобального или локального пространства имен).
В отличие от операции импортирования, функция exec читает и выполняет программный код файла без всяких проверок (один и тот же файл можно выполнить несколько раз в одном процессе); при выполнении файла не создается объект модуля; и, если функции явно не передаются словари пространств имен, операции присваивания в программном коде файла могут затирать переменные в области видимости, где находится вызов exec.